import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; import { authorizeAdminAccess, HIGH_RISK_ADMIN_CHALLENGE_TTL_MS, issueHighRiskAdminChallenge, issueHighRiskAdminProof, isSameOriginAdminMutation, verifyHighRiskAdminChallenge, verifyHighRiskAdminProof, } from "../src/lib/admin/auth-policy.ts"; import type { IdentityUser } from "../src/modules/identity/contracts.ts"; const now = 1_786_000_000_000; const context = { userId: "11111111-1111-4111-8111-111111111111", sessionId: "22222222-2222-4222-8222-222222222222", permission: "admin.users.manage_roles" as const, origin: "https://admin.staging.jyotisha.chat", }; const sessionToken = "better-auth-session-token-held-server-side"; const proofSecret = "admin-proof-secret-held-only-by-the-server"; function user(): IdentityUser { return { id: context.userId, email: "admin@example.com", emailVerified: true, name: "Admin", image: null, role: ["user"], twoFactorEnabled: false, }; } test("email OTP challenge is short-lived and bound to the requested permission and session", () => { const challenge = issueHighRiskAdminChallenge(context, proofSecret, sessionToken, now); assert.equal( verifyHighRiskAdminChallenge(challenge, context, proofSecret, sessionToken, now + 1_000), true, ); assert.equal(verifyHighRiskAdminChallenge( challenge, { ...context, permission: "billing.products.publish" }, proofSecret, sessionToken, now + 1_000, ), false); assert.equal(verifyHighRiskAdminChallenge( challenge, context, proofSecret, "rotated-session-token", now + 1_000, ), false); assert.equal(verifyHighRiskAdminChallenge( challenge, context, proofSecret, sessionToken, now + HIGH_RISK_ADMIN_CHALLENGE_TTL_MS, ), false); }); test("high-risk proof succeeds only for its live Better Auth session", () => { const proof = issueHighRiskAdminProof(context, proofSecret, sessionToken, now); assert.equal(verifyHighRiskAdminProof(proof, context, proofSecret, sessionToken, now + 1_000), true); assert.equal(verifyHighRiskAdminProof(proof, { ...context, sessionId: "wrong-session" }, proofSecret, sessionToken, now + 1_000), false); assert.equal(verifyHighRiskAdminProof( proof, { ...context, permission: "billing.products.publish" }, proofSecret, sessionToken, now + 1_000, ), false); assert.equal(verifyHighRiskAdminProof(proof, context, "wrong-server-secret-that-is-long-enough", sessionToken, now + 1_000), false); assert.equal(verifyHighRiskAdminProof(proof, context, proofSecret, sessionToken, now + 300_000), false); }); test("high-risk authorization rejects missing permission and cross-origin requests", () => { assert.deepEqual(authorizeAdminAccess(user(), [], context.permission), { allowed: false, status: 403 }); assert.equal(isSameOriginAdminMutation(context.origin, `${context.origin}/api/admin/reauth`), true); assert.equal(isSameOriginAdminMutation("https://evil.example", `${context.origin}/api/admin/reauth`), false); assert.equal(isSameOriginAdminMutation(null, `${context.origin}/api/admin/reauth`), false); }); test("reauth route consumes Better Auth email OTP and sets a scoped HttpOnly proof", () => { const route = readFileSync(new URL("../src/app/api/admin/reauth/route.ts", import.meta.url), "utf8"); const modal = readFileSync(new URL("../src/components/admin/reason-action-modal.tsx", import.meta.url), "utf8"); assert.match(route, /sendVerificationOTP/); assert.match(route, /verifyEmailOTP/); assert.match(route, /type: "email-verification"/); assert.match(route, /issueHighRiskAdminChallenge/); assert.match(route, /verifyHighRiskAdminChallenge/); assert.match(route, /HIGH_RISK_ADMIN_CHALLENGE_COOKIE/); assert.match(route, /verifyHighRiskAdminChallenge[\s\S]*verifyEmailOTP/); assert.match(route, /httpOnly: true/); assert.match(route, /sameSite: "strict"/); assert.match(route, /secure: true/); assert.match(route, /path: "\/api\/admin"/); assert.match(route, /adminProofSigningSecret\(\)/); assert.doesNotMatch(route, /console\.|otp.*log|log.*otp/i); assert.match(modal, /reauthPermission/); assert.match(modal, /action: "request"/); assert.match(modal, /action: "verify"/); assert.match(modal, /邮箱验证码/); });