fix(auth): remove manual session.save() — was suppressing Set-Cookie header

Login was returning 200 + correct user JSON + writing a row to the
sessions table, but emitting zero Set-Cookie headers. Root cause:

  session.regenerate() → set fields → session.save() → res.json()

Calling session.save() manually writes the store but bypasses
express-session's res.end() hook, which is the only path that adds
the Set-Cookie header to the response. The cookie was never sent to
the browser even though the session existed server-side — hence the
redirect loop.

Fix: remove the manual save(). Set the session fields and call
res.json() directly inside regenerate()'s callback; express-session
handles store write + Set-Cookie automatically on res.end().
This commit is contained in:
opencode 2026-05-27 02:59:15 +00:00 committed by ZGaetano
parent 5de1e3dc3d
commit e71c330bdd

View file

@ -127,10 +127,11 @@ router.post('/login', async (req, res, next) => {
// Successful login — clear any accumulated failed attempts // Successful login — clear any accumulated failed attempts
clearAttempts(req, username); clearAttempts(req, username);
// Regenerate session ID to prevent fixation attacks. // Regenerate session ID to prevent fixation attacks, then let
// Explicit save() + log on success/failure so a broken session store // express-session write Set-Cookie on its own res.end() hook.
// (missing table, PG read-only, etc.) doesn't silently 200 with no // Do NOT call session.save() manually — that writes the store but
// persisted session — the symptom that caused the redirect loop. // bypasses the middleware's header-writing path, which is why the
// Set-Cookie header was missing even though the session row existed.
req.session.regenerate((err) => { req.session.regenerate((err) => {
if (err) { if (err) {
console.error('[auth] session.regenerate failed:', err); console.error('[auth] session.regenerate failed:', err);
@ -139,18 +140,12 @@ router.post('/login', async (req, res, next) => {
req.session.userId = user.id; req.session.userId = user.id;
req.session.username = user.username; req.session.username = user.username;
req.session.role = user.role; req.session.role = user.role;
req.session.save((saveErr) => { console.log(`[auth] login ok user=${user.username} sid=${req.sessionID?.slice(0,8) || '?'} secure=${req.secure} proto=${req.protocol}`);
if (saveErr) { res.json({
console.error('[auth] session.save failed:', saveErr); id: user.id,
return next(saveErr); username: user.username,
} display_name: user.display_name,
console.log(`[auth] login ok user=${user.username} sid=${req.sessionID?.slice(0,8) || '?'} secure=${req.secure} proto=${req.protocol}`); role: user.role,
res.json({
id: user.id,
username: user.username,
display_name: user.display_name,
role: user.role,
});
}); });
}); });
} catch (err) { } catch (err) {