From e71c330bdd9236012c08b0e8e714412fbfd00d4b Mon Sep 17 00:00:00 2001 From: opencode Date: Wed, 27 May 2026 02:59:15 +0000 Subject: [PATCH] =?UTF-8?q?fix(auth):=20remove=20manual=20session.save()?= =?UTF-8?q?=20=E2=80=94=20was=20suppressing=20Set-Cookie=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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(). --- services/mam-api/src/routes/auth.js | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/services/mam-api/src/routes/auth.js b/services/mam-api/src/routes/auth.js index 6ca4885..3c8d862 100644 --- a/services/mam-api/src/routes/auth.js +++ b/services/mam-api/src/routes/auth.js @@ -127,10 +127,11 @@ router.post('/login', async (req, res, next) => { // Successful login — clear any accumulated failed attempts clearAttempts(req, username); - // Regenerate session ID to prevent fixation attacks. - // Explicit save() + log on success/failure so a broken session store - // (missing table, PG read-only, etc.) doesn't silently 200 with no - // persisted session — the symptom that caused the redirect loop. + // Regenerate session ID to prevent fixation attacks, then let + // express-session write Set-Cookie on its own res.end() hook. + // Do NOT call session.save() manually — that writes the store but + // 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) => { if (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.username = user.username; req.session.role = user.role; - req.session.save((saveErr) => { - if (saveErr) { - console.error('[auth] session.save failed:', saveErr); - return next(saveErr); - } - console.log(`[auth] login ok user=${user.username} sid=${req.sessionID?.slice(0,8) || '?'} secure=${req.secure} proto=${req.protocol}`); - res.json({ - id: user.id, - username: user.username, - display_name: user.display_name, - role: user.role, - }); + console.log(`[auth] login ok user=${user.username} sid=${req.sessionID?.slice(0,8) || '?'} secure=${req.secure} proto=${req.protocol}`); + res.json({ + id: user.id, + username: user.username, + display_name: user.display_name, + role: user.role, }); }); } catch (err) {