mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
test(auth): give the MFA fixtures a Bearer transport
The disable-route suite only ever supplied a cookie-shaped context, which is why the token-id-as-session-id defect was invisible to it. It now covers both arms and asserts the credential shape handed to the session revoker, plus that the elevation is spent before the teardown and not spent when the factor code is wrong.
This commit is contained in:
1 parent
4cd0adc46d
commit
f1c977a7a7
4 files changed
+75
-2
No files matched your search
@@ -55,8 +55,10 @@ function req() {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(requireMfaManagementAuth).mockResolvedValue({
|
||||
transport: "cookie",
|
||||
user: { id: "u1" },
|
||||
session: { id: "sess-current" },
|
||||
commitElevation: vi.fn().mockResolvedValue(undefined),
|
||||
} as never);
|
||||
vi.mocked(verifyMfaFactor).mockResolvedValue({ ok: true } as never);
|
||||
vi.mocked(prisma.$transaction).mockImplementation(async (cb: unknown) => {
|
||||
@@ -74,7 +76,68 @@ describe("POST /api/auth/me/mfa/disable", () => {
|
||||
it("revokes other sessions + trusted devices (via destroyOtherSessions), keeping the current one", async () => {
|
||||
const res = await POST(req());
|
||||
expect(res.status).toBe(200);
|
||||
expect(destroyOtherSessions).toHaveBeenCalledWith("u1", "sess-current");
|
||||
expect(destroyOtherSessions).toHaveBeenCalledWith("u1", {
|
||||
kind: "session",
|
||||
sessionId: "sess-current",
|
||||
});
|
||||
});
|
||||
|
||||
it("spares the CALLER's own device login on the Bearer path", async () => {
|
||||
// A Bearer caller has no session row. The earlier revision passed
|
||||
// `session.id` — an ApiToken id on this arm — into a session-scoped query,
|
||||
// so the "keep the current one" exclusion excluded nothing: every browser
|
||||
// session went AND the caller's own refresh token was revoked, logging the
|
||||
// app out at its next rotation. A cookie-shaped fixture could never see it.
|
||||
vi.mocked(requireMfaManagementAuth).mockResolvedValue({
|
||||
transport: "bearer",
|
||||
user: { id: "u1" },
|
||||
apiTokenId: "token-row-1",
|
||||
accessTokenHash: "hash-of-caller-access-token",
|
||||
commitElevation: vi.fn().mockResolvedValue(undefined),
|
||||
} as never);
|
||||
|
||||
const res = await POST(req());
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(destroyOtherSessions).toHaveBeenCalledWith("u1", {
|
||||
kind: "accessToken",
|
||||
accessTokenHash: "hash-of-caller-access-token",
|
||||
});
|
||||
});
|
||||
|
||||
it("spends the elevation before tearing the factor down", async () => {
|
||||
const commitElevation = vi.fn().mockResolvedValue(undefined);
|
||||
vi.mocked(requireMfaManagementAuth).mockResolvedValue({
|
||||
transport: "bearer",
|
||||
user: { id: "u1" },
|
||||
apiTokenId: "token-row-1",
|
||||
accessTokenHash: "h",
|
||||
commitElevation,
|
||||
} as never);
|
||||
|
||||
await POST(req());
|
||||
|
||||
expect(commitElevation).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("does NOT spend the elevation when the factor code is wrong", async () => {
|
||||
const commitElevation = vi.fn().mockResolvedValue(undefined);
|
||||
vi.mocked(requireMfaManagementAuth).mockResolvedValue({
|
||||
transport: "bearer",
|
||||
user: { id: "u1" },
|
||||
apiTokenId: "token-row-1",
|
||||
accessTokenHash: "h",
|
||||
commitElevation,
|
||||
} as never);
|
||||
vi.mocked(verifyMfaFactor).mockResolvedValue({
|
||||
ok: false,
|
||||
replay: false,
|
||||
} as never);
|
||||
|
||||
const res = await POST(req());
|
||||
|
||||
expect(res.status).toBe(401);
|
||||
expect(commitElevation).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rejects a wrong current factor without revoking anything", async () => {
|
||||
|
||||
@@ -57,7 +57,10 @@ function verifyRequest(body: Record<string, unknown>): NextRequest {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(requireMfaManagementAuth).mockResolvedValue({
|
||||
transport: "cookie",
|
||||
user: USER,
|
||||
session: { id: "sess-1" },
|
||||
commitElevation: vi.fn().mockResolvedValue(undefined),
|
||||
} as never);
|
||||
vi.mocked(checkRateLimit).mockResolvedValue({ allowed: true } as never);
|
||||
});
|
||||
|
||||
@@ -157,7 +157,10 @@ describe("DELETE /api/auth/me/sessions (sign out everywhere)", () => {
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.data.sessionsRevoked).toBe(3);
|
||||
expect(destroyOtherSessions).toHaveBeenCalledWith("user-1", "sess-current");
|
||||
expect(destroyOtherSessions).toHaveBeenCalledWith("user-1", {
|
||||
kind: "session",
|
||||
sessionId: "sess-current",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user