import { createHmac, randomUUID } from "node:crypto"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { createApp } from "../../apps/api/src/app.js"; import { RegistrationService } from "../../apps/api/src/registration.js"; import { MockResendAdapter } from "../../apps/api/src/resend-adapter.js"; const roots: string[] = []; const services: RegistrationService[] = []; const now = Date.parse("2026-07-28T12:00:00.000Z"); const adminPepper = Buffer.alloc(32, 0xb1); const headers = { host: "127.0.0.1:43121", origin: "http://127.0.0.1:43121" }; function createHarness() { const root = mkdtempSync(join(tmpdir(), "dada-wp1-04-api-")); roots.push(root); const resend = new MockResendAdapter(); const registration = new RegistrationService({ adminAllowlistPepper: adminPepper, challengePepper: Buffer.alloc(32, 0xb2), clock: () => now, codeGenerator: () => "418205", currentPrivacyNoticeVersion: "p0a-registration-notice-v1", databasePath: join(root, "dada.sqlite3"), invitePepper: Buffer.alloc(32, 0xb3), resend, sessionPepper: Buffer.alloc(32, 0xb4), }); services.push(registration); return { registration, resend }; } function cookieValue(setCookie: string | string[] | undefined, name: string) { const entries = Array.isArray(setCookie) ? setCookie : [setCookie ?? ""]; const match = entries.find((entry) => entry.startsWith(`${name}=`)); if (!match) throw new Error(`Cookie ${name} was not returned.`); return match.split(";", 1)[0]; } afterEach(() => { for (const service of services.splice(0)) service.close(); for (const root of roots.splice(0)) rmSync(root, { force: true, recursive: true }); }); describe("TASK-WP1-04 admin auth API", () => { it("rejects before Resend, creates an admin session, and isolates both audiences", async () => { const { registration, resend } = createHarness(); const email = "api-admin@example.invalid"; registration.applySecureConfig({ adminAllowlistHashes: [createHmac("sha256", adminPepper).update(email).digest("hex").toUpperCase()], adminRecoveryHashes: [], secureConfigRevision: 1, }); const app = await createApp({ browserGate: false, networkBoundary: { allowTestPort: true }, registration }); const blocked = await app.inject({ headers, method: "POST", payload: { email: "blocked@example.invalid" }, url: "/api/v1/admin-auth/login/send", }); expect(blocked.statusCode).toBe(409); expect(blocked.json()).toMatchObject({ error: { details: { field_errors: [{ message_key: "admin.auth.not_allowed" }] } } }); expect(resend.calls).toHaveLength(0); const sent = await app.inject({ headers, method: "POST", payload: { email }, url: "/api/v1/admin-auth/login/send", }); expect(sent.statusCode).toBe(200); const flowCookie = cookieValue(sent.headers["set-cookie"], "dada_admin_auth_flow"); const code = resend.readLatestCode(email); const completed = await app.inject({ headers: { ...headers, cookie: flowCookie, "idempotency-key": "wp1-04-api-admin-complete-000000000001" }, method: "POST", payload: { registration_id: sent.json().registration_id, verification_code: code }, url: "/api/v1/admin-auth/login/complete", }); expect(completed.statusCode).toBe(200); expect(completed.json()).toMatchObject({ audience: "admin", status: "authenticated", admin: { role: "super_admin" } }); const adminCookie = cookieValue(completed.headers["set-cookie"], "dada_admin_session"); const session = await app.inject({ headers: { ...headers, cookie: adminCookie }, method: "GET", url: "/api/v1/admin-auth/session" }); expect(session.statusCode).toBe(200); expect(session.json()).toMatchObject({ audience: "admin", authenticated: true, notice_acknowledged: false }); const ordinaryUserId = randomUUID(); registration.database.prepare(` INSERT INTO users ( user_id, normalized_email, role, status, counts_toward_stage_limit, registration_id, created_at ) VALUES (?, 'api-user@example.invalid', 'user', 'active', 1, ?, ?) `).run(ordinaryUserId, randomUUID(), now); const ordinary = registration.issueAuthenticatedSession(ordinaryUserId, "user"); const userAtAdmin = await app.inject({ headers: { ...headers, cookie: `dada_admin_session=${ordinary.sessionToken}` }, method: "GET", url: "/api/v1/admin-auth/session", }); expect(userAtAdmin.statusCode).toBe(401); const adminAtUser = await app.inject({ headers: { ...headers, cookie: adminCookie }, method: "GET", url: "/api/v1/auth/session" }); expect(adminAtUser.statusCode).toBe(401); await app.close(); }); });