Reputation: 6220
I am using the official NuxtJS Auth Routes example to perform login using express-sessions, it works perfectly
app.js file
const express = require('express')
const session = require('express-session')
const app = express()
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
// session middleware
app.use(
session({
secret: 'super-secret-key',
resave: false,
saveUninitialized: false,
cookie: { maxAge: 60000 }
})
)
// Create express router
const router = express.Router()
// Transform req & res to have the same API as express
// So we can use res.status() & res.json()
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request)
Object.setPrototypeOf(res, app.response)
req.res = res
res.req = req
next()
})
// Add POST - /api/login
router.post('/login', (req, res) => {
if (req.body.username === 'demo' && req.body.password === 'demo') {
req.session.authUser = { username: 'demo' }
return res.json({ username: 'demo' })
}
res.status(401).json({ message: 'Bad credentials' })
})
// Add POST - /api/logout
router.post('/logout', (req, res) => {
delete req.session.authUser
res.json({ ok: true })
})
app.use('/api', router)
module.exports = app
The problem is when the cookie has expired, the user is still logged in on the front end. If they access an endpoint, they get logged out though because I am guessing nuxtServerInit gets called which unsets the user
store/index.js
import axios from 'axios'
export const state = () => ({
authUser: null
})
export const mutations = {
SET_USER(state, user) {
state.authUser = user
}
}
export const actions = {
// nuxtServerInit is called by Nuxt.js before server-rendering every page
nuxtServerInit({ commit }, { req }) {
if (req.session && req.session.authUser) {
commit('SET_USER', req.session.authUser)
}
},
async login({ commit }, { username, password }) {
try {
const { data } = await axios.post('/api/login', { username, password })
commit('SET_USER', data)
} catch (error) {
if (error.response && error.response.status === 401) {
throw new Error('Bad credentials')
}
throw error
}
},
async logout({ commit }) {
await axios.post('/api/logout')
commit('SET_USER', null)
}
}
Upvotes: 1
Views: 4854
Reputation: 1867
You can register a service worker to asynchronously listen for changes in the cookie store (that too without continuous polling!) This is fairly new and support for some browsers may not be there. Here is an article on it.
Alternatively you can use the Push API to notify the front end of expiry and call the logOut method. Your server should be able to track expiry and send a push.
If they access an endpoint, they get logged out though because I am guessing nuxtServerInit gets called which unsets the user.
Honestly, this is pretty much the default behavior on majority of websites. Unless you are building a webapp with security first in mind.
Upvotes: 1
Reputation: 6220
Very close to the answer here. The idea is to set req.session.expires when the user posts to login route. The problem at the moment is inside the async login method, how do you get access to req.session?
This is the server/index.js file
// Add POST - /api/login
router.post('/login', (req, res) => {
if (req.body.username === 'demo' && req.body.password === 'demo') {
req.session.authUser = { username: 'demo' }
req.session.expires = req.session.cookie.expires.getTime()
return res.json({ username: 'demo' })
}
res.status(401).json({ message: 'Bad credentials' })
})
This is the store/index.js file
export const actions = {
// nuxtServerInit is called by Nuxt.js before server-rendering every page
nuxtServerInit({ commit }, { req }) {
if (req.session && req.session.authUser) {
commit('SET_USER', req.session.authUser)
}
if (req.session && req.session.expires) {
commit('SET_EXPIRES', req.session.expires)
}
},
// How do you get req.session.expires HERE???
async login({ commit }, { username, password }) {
try {
const response = await this.$axios.post('/api/login', {
username,
password
})
console.log(response, response.request, response.request.session)
commit('SET_USER', response.data)
} catch (error) {
if (error.response && error.response.status === 401) {
throw new Error('Bad credentials')
}
throw error
}
},
async logout({ commit }) {
await this.$axios.post('/api/logout')
commit('SET_USER', null)
commit('SET_EXPIRES', null)
}
}
Upvotes: 0
Reputation: 1078
Short of having a websocket wait for a logout notice, you won't be able to immediately log the user out upon expiration. That's why it is waiting until you attempt to navigate again. If you want to instantly log the user out, you can create a Websocket that waits for a signal from a server upon cookie expiration.
https://github.com/nuxt/nuxt.js/tree/dev/examples/with-sockets
However, this is a specific use case. Most websites simply wait for the user to attempt another action that requires their token to be checked for expiration and then takes appropriate measures.
A simplified version of this can be done by tracking the expiration time left on the front-end in the store. Then you can use computed
property to check the constantly decrementing countdown of how much time the user has left. When this hits zero, you can run your mutations.
Upvotes: 1