Skip to content

Commit

Permalink
Feature: Mobile - Automatic logout after a not authenticated response.
Browse files Browse the repository at this point in the history
  • Loading branch information
dominikklein committed May 6, 2022
1 parent 97431f5 commit 94a1d82
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 20 deletions.
23 changes: 17 additions & 6 deletions app/frontend/apps/mobile/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@ import useAuthenticatedStore from '@common/stores/authenticated'
import useSessionIdStore from '@common/stores/session/id'
import useMetaTitle from '@common/composables/useMetaTitle'
import { useRoute, useRouter } from 'vue-router'

// TODO ... maybe show some special message, if the session was removed from a other place.
// unauthorized () {
// return !this.$store.getters.accessToken && this.$store.getters.authenticated;
// },
import { computed, watch } from 'vue'

const router = useRouter()
const route = useRoute()
Expand All @@ -37,7 +33,22 @@ useMetaTitle().initializeMetaTitle()
const applicationLoaded = useApplicationLoadedStore()
applicationLoaded.setLoaded()

// Add a watcher for authenticated change.
const invalidatedSession = computed(() => {
return !sessionId.value && authenticated.value
})

watch(invalidatedSession, () => {
authenticated.clearAuthentication()

router.replace({
name: 'Login',
params: {
invalidatedSession: '1',
},
})
})

// Add a watcher for authenticated changes (e.g. login/logout in a other browser tab).
authenticated.$subscribe((mutation, state) => {
if (state.value && !sessionId.value) {
sessionId.checkSession().then((sessionId) => {
Expand Down
16 changes: 15 additions & 1 deletion app/frontend/apps/mobile/views/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
<br />
<p v-on:click="logout">{{ i18n.t('Logout') }}</p>
<br />
<p v-on:click="goToTickets">Go to Tickets</p>
<br />
<p v-on:click="refetchConfig">refetchConfig</p>
<br />
<p v-on:click="fetchCurrentUser">fetchCurrentUser</p>
<br /><br />
<h1 class="text-lg mb-4">Configs:</h1>
<template v-if="config.value">
Expand All @@ -26,6 +30,7 @@ import useSessionUserStore from '@common/stores/session/user'
import { storeToRefs } from 'pinia'
import { useRouter } from 'vue-router'
import useApplicationConfigStore from '@common/stores/application/config'
import { useCurrentUserQuery } from '@common/graphql/api'

// TODO: Only testing for the notifications...
const { notify } = useNotifications()
Expand All @@ -45,7 +50,7 @@ const router = useRouter()

const logout = (): void => {
authenticated.logout().then(() => {
router.push('/login')
router.push('login')
})
}

Expand All @@ -54,4 +59,13 @@ const config = useApplicationConfigStore()
const refetchConfig = async (): Promise<void> => {
await config.getConfig()
}

const fetchCurrentUser = () => {
const { result } = useCurrentUserQuery({ fetchPolicy: 'no-cache' })
console.log('result', result)
}

const goToTickets = () => {
router.push('/tickets')
}
</script>
18 changes: 18 additions & 0 deletions app/frontend/apps/mobile/views/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,28 @@
</template>

<script setup lang="ts">
import useNotifications from '@common/composables/useNotifications'
import useApplicationConfigStore from '@common/stores/application/config'
import useAuthenticationStore from '@common/stores/authenticated'
import { useRouter } from 'vue-router'

interface Props {
invalidatedSession?: string
}

const props = defineProps<Props>()

// Output a hint, when the session is longer valid, maybe because because the session
// was deleted on the server.
if (props.invalidatedSession === '1') {
const { notify } = useNotifications()

notify({
message: __('The session is no longer valid. Please log in again.'),
type: 'warning',
})
}

const authentication = useAuthenticationStore()
const loginFormValues = {
login: '',
Expand Down
9 changes: 8 additions & 1 deletion app/frontend/apps/mobile/views/TicketOverview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@
<template>
<div>
<h1>{{ i18n.t('Ticket Overview') }}</h1>
<p v-on:click="goTo">Go to link Home</p>
</div>
</template>

<script setup lang="ts">
// TODO ...
import { useRouter } from 'vue-router'

const router = useRouter()

const goTo = () => {
router.push('/')
}
</script>
8 changes: 5 additions & 3 deletions app/frontend/common/server/apollo/link/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
GraphQLErrorExtensionsHandler,
GraphQLErrorTypes,
} from '@common/types/error'
import useSessionIdStore from '@common/stores/session/id'

const errorLink = onError(({ graphQLErrors, networkError, operation }) => {
const errorContext = getErrorContext(operation)
Expand All @@ -31,9 +32,10 @@ const errorLink = onError(({ graphQLErrors, networkError, operation }) => {
operation.operationName !== 'sessionId' &&
type === GraphQLErrorTypes.NotAuthorized
) {
// Session Invalid:
// TODO ...do something...
log.warn('Session invalid...')
// Reset sessionId after an unathenticated error type.
useSessionIdStore().value = null

log.warn('Session invalid, trigger logout and show login page.')
}
})
}
Expand Down
20 changes: 13 additions & 7 deletions app/frontend/common/stores/authenticated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,21 @@ const useAuthenticatedStore = defineStore('authenticated', {
const result = await logoutMutation.send()

if (result?.logout?.success) {
await apolloClient.clearStore()
await this.clearAuthentication()
}
},

const sessionId = useSessionIdStore()
sessionId.value = null
async clearAuthentication(): Promise<void> {
await apolloClient.clearStore()

// Refresh the config after logout, to have only the non authenticated version.
await useApplicationConfigStore().resetAndGetConfig()
const sessionId = useSessionIdStore()
sessionId.value = null
this.value = false

// TODO... check for other things which must be removed/cleared during a logout.
}
// Refresh the config after logout, to have only the non authenticated version.
await useApplicationConfigStore().resetAndGetConfig()

// TODO... check for other things which must be removed/cleared during a logout.
},

async login(login: string, password: string): Promise<void> {
Expand All @@ -52,6 +57,7 @@ const useAuthenticatedStore = defineStore('authenticated', {
if (newSessionId) {
const sessionId = useSessionIdStore()
sessionId.value = newSessionId
this.value = true

await Promise.all([
useApplicationConfigStore().getConfig(),
Expand Down
4 changes: 3 additions & 1 deletion app/frontend/common/types/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import { GraphQLErrorExtensions } from 'graphql'
export enum GraphQLErrorTypes {
UnkownError = 'Exceptions::UnkownError',
NetworkError = 'Exceptions::NetworkError',

// This exception actually means 'NotAuthenticated'
NotAuthorized = 'Exceptions::NotAuthorized',
}

export type GraphQLErrorTypeKeys = keyof GraphQLErrorTypes | 'test'
export type GraphQLErrorTypeKeys = keyof GraphQLErrorTypes

export interface GraphQLErrorExtensionsHandler {
type: GraphQLErrorTypes
Expand Down
6 changes: 5 additions & 1 deletion config/initializers/content_security_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@
policy.frame_src 'www.youtube.com', 'player.vimeo.com'

if Rails.env.development?
websocket_uri = proc do
"ws://localhost:#{Setting.get('websocket_port')}"
end

policy.script_src :self, :unsafe_eval, :unsafe_inline
policy.connect_src :self, :https, "http://#{ViteRuby.config.host_with_port}", "ws://#{ViteRuby.config.host_with_port}"
policy.connect_src :self, :https, "http://#{ViteRuby.config.host_with_port}", "ws://#{ViteRuby.config.host_with_port}", websocket_uri
end
end

Expand Down
4 changes: 4 additions & 0 deletions i18n/zammad.pot
Original file line number Diff line number Diff line change
Expand Up @@ -9048,6 +9048,10 @@ msgstr ""
msgid "The server settings could not be automatically detected. Please configure them manually."
msgstr ""

#: app/frontend/apps/mobile/views/Login.vue
msgid "The session is no longer valid. Please log in again."
msgstr ""

#: app/models/store/file.rb
msgid "The setting 'storage_provider' was not configured."
msgstr ""
Expand Down

0 comments on commit 94a1d82

Please sign in to comment.