test(auth): retarget the MFA route mocks at the shared gate

The three unit suites that stubbed `requireCookieAuth` / `requireFreshMfa`
directly now stub `requireMfaManagementAuth`, and the password-change suite
stubs the elevation revocation the route gained. Adds the unit checks that need
no database: the elevation TTL is pinned to the cookie path's step-up window so
the two cannot drift apart, the minted value is a prefixed CSPRNG secret whose
plaintext never reaches the row, and a value carrying another credential's
prefix is refused without costing a query.
This commit is contained in:
Marc-André Bombeck committed 2026-07-19 13:14:38 +02:00
1 parent 433d983e60
commit ba45a25cb0
5 files changed
+132 -8

No files matched your search

@@ -7,7 +7,7 @@ vi.mock("@/lib/api-handler", async () => {
);
return {
...actual,
requireFreshMfa: vi.fn(),
requireMfaManagementAuth: vi.fn(),
};
});
@@ -39,7 +39,7 @@ vi.mock("@/lib/auth/mfa/verify-factor", () => ({
vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() }));
import { POST } from "../route";
import { requireFreshMfa } from "@/lib/api-handler";
import { requireMfaManagementAuth } from "@/lib/api-handler";
import { prisma } from "@/lib/db";
import { destroyOtherSessions } from "@/lib/auth/session";
import { verifyMfaFactor } from "@/lib/auth/mfa/verify-factor";
@@ -54,7 +54,7 @@ function req() {
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(requireFreshMfa).mockResolvedValue({
vi.mocked(requireMfaManagementAuth).mockResolvedValue({
user: { id: "u1" },
session: { id: "sess-current" },
} as never);
@@ -8,7 +8,7 @@ vi.mock("@/lib/auth/mfa-enrollment", () => ({
vi.mock("@/lib/api-handler", () => ({
apiHandler: (fn: unknown) => fn,
requireCookieAuth: vi.fn(),
requireMfaManagementAuth: vi.fn(),
}));
vi.mock("@/lib/db", () => ({
@@ -33,7 +33,7 @@ vi.mock("@/lib/logging/context", () => ({ annotate: vi.fn() }));
import { POST as REGISTER_OPTIONS } from "../options/route";
import { POST as REGISTER_VERIFY } from "../verify/route";
import { requireCookieAuth } from "@/lib/api-handler";
import { requireMfaManagementAuth } from "@/lib/api-handler";
import { prisma } from "@/lib/db";
import { checkRateLimit } from "@/lib/rate-limit";
import {
@@ -56,7 +56,9 @@ function verifyRequest(body: Record<string, unknown>): NextRequest {
beforeEach(() => {
vi.resetAllMocks();
vi.mocked(requireCookieAuth).mockResolvedValue({ user: USER } as never);
vi.mocked(requireMfaManagementAuth).mockResolvedValue({
user: USER,
} as never);
vi.mocked(checkRateLimit).mockResolvedValue({ allowed: true } as never);
});
@@ -24,6 +24,12 @@ vi.mock("@/lib/db", () => ({
},
}));
// v1.30.34 — the route drops the account's step-up elevations alongside its
// sessions, so the rotation cannot leave a proof of the old credential standing.
vi.mock("@/lib/auth/step-up", () => ({
revokeStepUpElevations: vi.fn().mockResolvedValue(undefined),
}));
vi.mock("@/lib/auth/session", () => ({
getSession: vi.fn(),
destroyAllSessions: vi.fn().mockResolvedValue(undefined),