From 3c8d344ddd23974c9cf0f5f63edd6cd07817fee9 Mon Sep 17 00:00:00 2001 From: Paul Nothaft Date: Sun, 5 Apr 2026 18:33:46 +0200 Subject: [PATCH] fix: resolve redirect loop after mandatory password change (#263) After changing password, the backend sets password_changed_at which invalidates the old JWT token. But the frontend still holds the old token in the HttpOnly cookie, so the next session check returns 401, triggering an infinite redirect loop between /admin/login and /admin/dashboard. Fix: issue a new JWT token cookie after successful password change so the session remains valid without requiring re-login. --- backend/src/routes/adminAuth.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/backend/src/routes/adminAuth.js b/backend/src/routes/adminAuth.js index 18fe5852..9d03a809 100644 --- a/backend/src/routes/adminAuth.js +++ b/backend/src/routes/adminAuth.js @@ -1,5 +1,6 @@ const express = require('express'); const bcrypt = require('bcrypt'); +const jwt = require('jsonwebtoken'); const { body } = require('express-validator'); const { db, logActivity } = require('../database/db'); const { adminAuth } = require('../middleware/auth'); @@ -7,6 +8,7 @@ const { endSession } = require('../middleware/sessionTimeout'); const { validatePasswordStrength } = require('../utils/passwordGenerator'); const { handleAsync, validateRequest, successResponse } = require('../utils/routeHelpers'); const { NotFoundError, ConflictError, ValidationError } = require('../utils/errors'); +const { setAdminAuthCookie } = require('../utils/tokenUtils'); const router = express.Router(); // Get admin profile @@ -133,6 +135,20 @@ router.post('/change-password', [ updated_at: now }); + // Issue a new token so the session remains valid after password_changed_at invalidated the old one + const newToken = jwt.sign({ + id: user.id, + username: user.username, + type: 'admin', + role: user.role_name, + loginTime: Date.now() + }, process.env.JWT_SECRET, { + expiresIn: '24h', + issuer: 'picpeak-auth' + }); + + setAdminAuthCookie(res, newToken); + // Log activity await logActivity('password_changed', { admin_id: userId },