From aba2c7f4706e30a64f15769bf0de744ba1cf1cd2 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Sun, 10 Dec 2017 22:57:57 +0100
Subject: [PATCH] Automatic token refresh and login redirect

The JWT Token is now refreshed upon each route change. If the token is expired the user will be logged out and redirected to the login page.
---
 frontend/src/components/Login.vue |  4 +++-
 frontend/src/router/index.js      | 19 ++++++++++++++++++-
 frontend/src/store/store.js       | 18 +++++++++++++++++-
 3 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/frontend/src/components/Login.vue b/frontend/src/components/Login.vue
index 1344def4..021cb660 100644
--- a/frontend/src/components/Login.vue
+++ b/frontend/src/components/Login.vue
@@ -51,13 +51,15 @@
       ...mapActions([
         'getJWTToken',
         'getExamModule',
-        'getUserRole'
+        'getUserRole',
+        'getJWTTimeDelta'
       ]),
       submit () {
         this.getJWTToken(this.credentials).then(() => {
           this.$router.push('/student/')
           this.getExamModule()
           this.getUserRole()
+          this.getJWTTimeDelta()
         }).catch(() => {
           this.error = this.$store.state.error
         })
diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js
index 32587176..6569eb17 100644
--- a/frontend/src/router/index.js
+++ b/frontend/src/router/index.js
@@ -1,5 +1,6 @@
 import Vue from 'vue'
 import Router from 'vue-router'
+import store from '../store/store'
 import Login from '@/components/Login'
 import StudentPage from '@/components/student/StudentPage'
 import StudentLayout from '@/components/student/StudentLayout'
@@ -11,7 +12,7 @@ import AnnotatedSubmission from '@/components/submission_notes/AnnotatedSubmissi
 
 Vue.use(Router)
 
-export default new Router({
+const router = new Router({
   routes: [
     {
       path: '/',
@@ -55,3 +56,19 @@ export default new Router({
     }
   ]
 })
+
+router.beforeEach((to, from, next) => {
+  if (from.path === '/') {
+    next()
+  } else {
+    const now = new Date()
+    if (now - store.state.logInTime > store.state.jwtTimeDelta * 1000) {
+      store.dispatch('logout').then(() => next('/'))
+    } else {
+      store.dispatch('refreshJWTToken')
+      next()
+    }
+  }
+})
+
+export default router
diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js
index 9d5acae0..21490864 100644
--- a/frontend/src/store/store.js
+++ b/frontend/src/store/store.js
@@ -16,13 +16,15 @@ const store = new Vuex.Store({
   state: {
     token: '',
     loggedIn: false,
+    logInTime: {},
     username: '',
+    jwtTimeDelta: 0,
     userRole: '',
     error: '',
     examInstance: ''
   },
   getters: {
-    gradySpeak: state => {
+    gradySpeak: () => {
       return gradySays[Math.floor(Math.random() * gradySays.length)]
     }
   },
@@ -32,8 +34,12 @@ const store = new Vuex.Store({
     },
     'SET_JWT_TOKEN': function (state, token) {
       state.token = token
+      state.logInTime = new Date()
       ax.defaults.headers.common['Authorization'] = 'JWT ' + token
     },
+    'SET_JWT_TIME_DELTA': function (state, timeDelta) {
+      state.jwtTimeDelta = timeDelta
+    },
     'LOGIN': function (state, username) {
       state.loggedIn = true
       state.username = username
@@ -66,6 +72,16 @@ const store = new Vuex.Store({
         }
       }
     },
+    refreshJWTToken (context) {
+      ax.post('/api-token-refresh/', {token: context.state.token}).then(response => {
+        context.commit('SET_JWT_TOKEN', response.data.token)
+      })
+    },
+    getJWTTimeDelta (context) {
+      ax.get('api/jwt-time-delta/').then(response => {
+        context.commit('SET_JWT_TIME_DELTA', response.data.timeDelta)
+      })
+    },
     getUserRole (context) {
       ax.get('api/user-role/').then(response => context.commit('SET_USER_ROLE', response.data.role))
     },
-- 
GitLab