diff --git a/frontend/src/App.vue b/frontend/src/App.vue index a981b10210c5004e134762f2f34ea8517da399d3..5e7938364f17537f5b0c9439fdce81310d9522e5 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -3,93 +3,17 @@ <v-app> <notifications/> <router-view/> - <v-dialog - persistent - width="fit-content" - v-model="logoutDialog" - > - <v-card> - <v-card-title class="headline"> - You'll be logged out! - </v-card-title> - <v-card-text> - Due to inactivity you'll be logged out in a couple of moments.<br/> - Any unsaved work will be lost. - Click Continue to stay logged in. - </v-card-text> - <v-card-actions> - <v-btn flat color="grey lighten-0" - @click="logout" - >Logout now</v-btn> - <v-spacer/> - <v-btn flat color="blue darken-2" - @click="continueWork" - >Continue</v-btn> - </v-card-actions> - </v-card> - </v-dialog> + <auto-logout/> </v-app> </div> </template> <script> - import {mapState} from 'vuex' + import AutoLogout from '@/components/AutoLogout' export default { - name: 'app', - data () { - return { - timer: 0, - logoutDialog: false - } - }, - computed: { - ...mapState([ - 'lastAppInteraction' - ]), - ...mapState({ - lastTokenRefreshTry: state => state.authentication.lastTokenRefreshTry, - refreshingToken: state => state.authentication.refreshingToken, - jwtTimeDelta: state => state.authentication.jwtTimeDelta - }) - }, - methods: { - logout () { - this.logoutDialog = false - this.$store.dispatch('logout') - }, - continueWork () { - this.$store.dispatch('refreshJWT') - this.logoutDialog = false - } - }, - watch: { - lastAppInteraction: function (val) { - const timeSinceLastRefresh = Date.now() - this.lastTokenRefreshTry - const timeDelta = this.jwtTimeDelta - // refresh jwt if it's older than 20% of his maximum age - if (this.$route.name !== 'login' && timeSinceLastRefresh > timeDelta * 0.2 && - !this.refreshingToken) { - this.$store.dispatch('refreshJWT') - } - } - }, - mounted () { - const oneAndHalfMinute = 90 * 1e3 - this.timer = setInterval(() => { - if (this.$route.name !== 'login' && this.$store.getters.isLoggedIn) { - if (Date.now() > this.lastTokenRefreshTry + this.jwtTimeDelta) { - this.logoutDialog = false - this.$store.dispatch('logout', "You've been logged out due to inactivity.") - } else if (Date.now() + oneAndHalfMinute > this.lastTokenRefreshTry + this.jwtTimeDelta) { - this.logoutDialog = true - } - } - }, 5 * 1e3) - }, - beforeDestroy () { - clearInterval(this.timer) - } + components: {AutoLogout}, + name: 'app' } </script> diff --git a/frontend/src/api.js b/frontend/src/api.js index 26799adabe41b99cebce55c8b9acfd1eec67fe6c..ead1f457c9ac0e771f77a540cee95729e145b3c2 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -26,7 +26,7 @@ export async function fetchJWT (credentials) { export async function refreshJWT (token) { const newToken = (await ax.post('/api/refresh-token/', {token})).data.token ax.defaults.headers['Authorization'] = `JWT ${newToken}` - return token + return newToken } export async function fetchJWTTimeDelta () { diff --git a/frontend/src/components/AutoLogout.vue b/frontend/src/components/AutoLogout.vue new file mode 100644 index 0000000000000000000000000000000000000000..e035b42bdcaa72adc8498b74d9147a11b1e2033e --- /dev/null +++ b/frontend/src/components/AutoLogout.vue @@ -0,0 +1,92 @@ +<template> + <v-dialog + persistent + width="fit-content" + v-model="logoutDialog" + > + <v-card> + <v-card-title class="headline"> + You'll be logged out! + </v-card-title> + <v-card-text> + Due to inactivity you'll be logged out in a couple of moments.<br/> + Any unsaved work will be lost. + Click Continue to stay logged in. + </v-card-text> + <v-card-actions> + <v-btn flat color="grey lighten-0" + @click="logout" + >Logout now</v-btn> + <v-spacer/> + <v-btn flat color="blue darken-2" + @click="continueWork" + >Continue</v-btn> + </v-card-actions> + </v-card> + </v-dialog> +</template> + +<script> + import {mapState} from 'vuex' + + export default { + name: 'auto-logout', + data () { + return { + timer: 0, + logoutDialog: false + } + }, + computed: { + ...mapState([ + 'lastAppInteraction' + ]), + ...mapState({ + lastTokenRefreshTry: state => state.authentication.lastTokenRefreshTry, + refreshingToken: state => state.authentication.refreshingToken, + jwtTimeDelta: state => state.authentication.jwtTimeDelta + }) + }, + methods: { + logout () { + this.logoutDialog = false + this.$store.dispatch('logout') + }, + continueWork () { + this.$store.dispatch('refreshJWT') + this.logoutDialog = false + } + }, + watch: { + lastAppInteraction: function (val) { + const timeSinceLastRefresh = Date.now() - this.lastTokenRefreshTry + const timeDelta = this.jwtTimeDelta + // refresh jwt if it's older than 20% of his maximum age + if (this.$route.name !== 'login' && timeSinceLastRefresh > timeDelta * 0.2 && + !this.refreshingToken) { + this.$store.dispatch('refreshJWT') + } + } + }, + mounted () { + this.timer = setInterval(() => { + const timeToLogOutDialog = Math.min(600 * 1e3, + this.jwtTimeDelta ? this.jwtTimeDelta * 0.3 : Infinity) + if (this.$route.name !== 'login' && this.$store.getters.isLoggedIn) { + if (Date.now() > this.lastTokenRefreshTry + this.jwtTimeDelta) { + this.logoutDialog = false + this.$store.dispatch('logout', "You've been logged out due to inactivity.") + } else if (Date.now() + timeToLogOutDialog > this.lastTokenRefreshTry + this.jwtTimeDelta) { + this.logoutDialog = true + } + } + }, 5 * 1e3) + }, + beforeDestroy () { + clearInterval(this.timer) + } + } +</script> + +<style> +</style> diff --git a/frontend/src/store/modules/authentication.js b/frontend/src/store/modules/authentication.js index 4720ff5b676c509c398b9035fbc7c0e92b301986..64afb26009b66ddedab204f55ead60b023fe5287 100644 --- a/frontend/src/store/modules/authentication.js +++ b/frontend/src/store/modules/authentication.js @@ -1,4 +1,4 @@ -import {fetchJWT, fetchJWTTimeDelta, fetchUserRole, refreshJWT} from '@/api' +import * as api from '@/api' import gradySays from '../grady_speak' function initialState () { @@ -75,7 +75,7 @@ const authentication = { actions: { async getJWT (context, credentials) { try { - const token = await fetchJWT(credentials) + const token = await api.fetchJWT(credentials) context.commit(authMut.SET_USERNAME, credentials.username) context.commit(authMut.SET_JWT_TOKEN, token) } catch (error) { @@ -96,7 +96,7 @@ const authentication = { async refreshJWT ({state, commit}) { commit(authMut.SET_REFRESHING_TOKEN, true) try { - const token = await refreshJWT(state.token) + const token = await api.refreshJWT(state.token) commit(authMut.SET_JWT_TOKEN, token) } finally { commit(authMut.SET_REFRESHING_TOKEN, false) @@ -105,7 +105,7 @@ const authentication = { }, async getUserRole ({commit}) { try { - const userRole = await fetchUserRole() + const userRole = await api.fetchUserRole() commit(authMut.SET_USER_ROLE, userRole) } catch (err) { commit(authMut.SET_MESSAGE, "You've been logged out.") @@ -113,7 +113,7 @@ const authentication = { }, async getJWTTimeDelta ({commit}) { try { - const delta = await fetchJWTTimeDelta() + const delta = await api.fetchJWTTimeDelta() // multiply by 1000 to convert to ms commit(authMut.SET_JWT_TIME_DELTA, delta * 1000) } catch (err) {