From 12844a92067117c3836d903bc27cc631b56f6369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 19 Jul 2026 11:37:15 +0200 Subject: [PATCH] test(auth): bind the rotated cookie through its hash after a password change The assertion compared the cookie to the new session row's id, which only held while the id WAS the cookie. Check the stored hash instead: it proves the cookie authenticates the surviving row and that the row id never reaches the client. --- tests/integration/auth-password-change.test.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/integration/auth-password-change.test.ts b/tests/integration/auth-password-change.test.ts index d3000ff69..44bc9f7bb 100644 --- a/tests/integration/auth-password-change.test.ts +++ b/tests/integration/auth-password-change.test.ts @@ -21,6 +21,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { cookieJar, headerJar } from "./mock-next-headers"; import { getPrismaClient, truncateAllTables } from "./setup"; +import { hashToken } from "@/lib/auth/hmac"; vi.mock("next/headers", async () => { const { cookieJar, headerJar } = await import("./mock-next-headers"); @@ -140,7 +141,14 @@ describe("POST /api/auth/password (real Postgres)", () => { expect(remainingSessions).toHaveLength(1); expect(remainingSessions[0].id).not.toBe(sessionId); expect(remainingSessions[0].id).not.toBe(siblingSession.id); - expect(cookieJar.get("healthlog_session")).toBe(remainingSessions[0].id); + + // v1.30.32 — the cookie carries the session secret, not the row id, so + // bind them through the stored hash instead. Strictly stronger than the + // old identity check: it proves the cookie actually authenticates the + // surviving row, and that the row id never leaves the server. + const rotatedCookie = cookieJar.get("healthlog_session")!; + expect(rotatedCookie).not.toBe(remainingSessions[0].id); + expect(remainingSessions[0].tokenHash).toBe(hashToken(rotatedCookie)); // The API token is flipped to revoked rather than deleted so the // audit trail survives.