Browse Source

Auto-renew session, notify if logged out

Alan Colon 7 years ago
parent
commit
f58e287644
4 changed files with 86 additions and 2 deletions
  1. 45 1
      app/api-service.js
  2. 2 1
      lib/controllers/auth/index.js
  3. 38 0
      lib/controllers/auth/renew.js
  4. 1 0
      lib/routes.js

+ 45 - 1
app/api-service.js

@@ -1,6 +1,6 @@
 const decode = require('jsonwebtoken/decode')
 const app = require('./app')
-app.service('api', function($http) {
+app.service('api', function($http, $location, $mdToast) {
   window.api = this
   this.opts = {
     headers: {},
@@ -41,6 +41,24 @@ app.service('api', function($http) {
     clearUser()
   }
 
+  this.renew = async () => {
+    console.info('Renewing session...')
+    const res = await this.post('/api/auth/renew', {})
+    setUser(res.user, res.token)
+  }
+
+  this.checkRenew = async () => {
+    if (!this.user) {
+      throw new Error('Not logged in')
+    }
+    try {
+      await this.renew()
+    } catch (e) {
+      clearUser()
+      throw e
+    }
+  }
+
   this.restore = () => {
     const token = localStorage.getItem('token')
     const userJson = localStorage.getItem('user')
@@ -56,6 +74,32 @@ app.service('api', function($http) {
 
   this.restore()
 
+  const renewTimer = () => {
+    if (this.user) {
+      this.renew().catch(err => {
+          console.warn('Login session could not be renewed:', err)
+          $mdToast.show(
+            $mdToast.simple()
+              .textContent(`You are not logged in: ${err.statusText || err.message}.`)
+              .action('Return to login page')
+              .hideDelay(60000)
+              .position('top middle')
+              .highlightAction(true)
+              .highlightClass('md-warn')
+          )
+            .then(res => {
+              if (res === 'ok') {
+                clearUser()
+                $location.url('/login')
+              }
+            })
+      })
+    }
+  }
+
+  setInterval(renewTimer, 15000)
+  renewTimer()
+
   this.crud = (apiPrefix) => ({
     list: () => this.get(apiPrefix),
     create: data => this.post(apiPrefix, data),

+ 2 - 1
lib/controllers/auth/index.js

@@ -3,5 +3,6 @@ module.exports = {
   decode: require('./decode'),
   verify: require('./verify'),
   permissions: require('./permissions'),
-  check: require('./check')
+  check: require('./check'),
+  renew: require('./renew')
 }

+ 38 - 0
lib/controllers/auth/renew.js

@@ -0,0 +1,38 @@
+const _ = require('lodash')
+const config = require('../../../config')
+const { User, Session } = require('../../database')
+const JWT = require('jsonwebtoken')
+const aguid = require('aguid')
+
+
+module.exports = {
+  post: async (req, res) => {
+    if (!req.user) return res.status(403).send('Not logged in')
+    const user = await User.findOne({where: {id: req.user.id}})
+    if (!user) return res.status(403).send(`Could not find user ${req.user.id}`)
+    const session = await Session.findOne({ where: {id: req.claims.sid}})
+    if (!session) return res.status(403).send(`Could not find session ${req.claims.sid}`)
+    const permissions = _.chain(await user.getRoles({paranoid: !config.hiddenRoles}))
+      .map(role => (role.permissions || '').split(','))
+      .flatten()
+      .uniq()
+      .map(permission => [permission, 1])
+      .fromPairs()
+      .value()
+    const sid = req.claims.sid
+    const exp = Math.floor(Date.now()/1000) + config.auth.jwtExpires
+    session.endAt = exp
+    await session.save()
+    const token = JWT.sign({
+      sid,
+      exp,
+      ...permissions,
+      user: user.sanitize()
+    }, config.auth.jwtSecret);
+    return res.status(200).send({
+      user: user.sanitize(),
+      token
+    })
+
+  }
+}

+ 1 - 0
lib/routes.js

@@ -6,6 +6,7 @@ module.exports = app => {
   app.post('/api/auth/login', asyncHandler(C.auth.login.post))
   app.get('/api/auth/check', asyncHandler(C.auth.check.get))
   app.get('/api/auth/permissions', asyncHandler(C.auth.permissions.list))
+  app.post('/api/auth/renew', asyncHandler(C.auth.renew.post))
   crudRoutes({ app, controller: C.user, pascalName: 'User' })
   crudRoutes({ app, controller: C.role, pascalName: 'Role' })
 }