Re-validate JWT against the DB in isLoggedIn (#12)

isLoggedIn trusted id and role straight from the JWT and never
re-checked the database, so a demoted admin kept their old role and a
deleted user kept a working session until the token expired (up to
JWT_EXPIRES_IN). This also undercut the "last admin" guards.

isLoggedIn now loads the user from the DB by the token's id on every
request: a missing user returns 401 (deleted), and req.user carries the
fresh DB row so the current role is always used downstream.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 21:16:43 -05:00
parent 52c74db825
commit 8ad18140d0

View File

@@ -2,6 +2,7 @@ const jwt = require('jsonwebtoken')
require('dotenv').config() require('dotenv').config()
const log = require('./logger')('auth') const log = require('./logger')('auth')
const users = require('../model/users/users.model')
const JWT_SECRET = process.env.JWT_SECRET const JWT_SECRET = process.env.JWT_SECRET
const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '1d' const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '1d'
@@ -77,12 +78,22 @@ function getUserFromRequest(req) {
return verifyToken(token) return verifyToken(token)
} }
// Gate middleware for protected (admin) routes. // Gate middleware for protected (admin) routes. Re-validates the token against
function isLoggedIn(req, res, next) { // the database on every request so a demoted or deleted user loses access
const user = getUserFromRequest(req) // immediately, instead of keeping their old role (or a working session) until
if (!user) return res.status(401).json({ message: 'Unauthorized' }) // the JWT expires. req.user carries the fresh DB row, not the token payload.
req.user = user async function isLoggedIn(req, res, next) {
return next() const decoded = getUserFromRequest(req)
if (!decoded) return res.status(401).json({ message: 'Unauthorized' })
try {
const user = await users.getById(decoded.id)
if (!user) return res.status(401).json({ message: 'Unauthorized' }) // deleted since token issued
req.user = user
return next()
} catch (err) {
log.error('isLoggedIn', err)
return res.status(500).json({ message: 'Internal Server Error' })
}
} }
module.exports = { module.exports = {