diff --git a/frontend/src/components/Login.vue b/frontend/src/components/Login.vue
index 1344def47c2964d613a4014abdba0d1ff387146e..021cb6600a9e2ad80eb017d6b98d4b7828f8bbd0 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 32587176fc476c53522d50b320d5e90e919d5442..6569eb171384c6ab28ab899414c1ddfb858c6799 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 9d5acae05dfddfde951986a929c079f9e46be3c2..214908640b8247ea79825a1656806e5b5318d392 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))
     },