/** * Authorisation enforced inside the handlers. * * The middleware is the first line: it covers every /admin page and * /api/admin route at the edge. This is the second — because a matcher is a * string, strings get edited, and a route quietly falling outside it should * not be the same thing as a route with no access control. */ import { authorize } from './authorize'; import { auth } from './index'; import type { Role } from './roles'; export type Caller = { email: string; role: Role }; /** * Resolves the caller and applies the rule. Returns either the caller or the * Response to send back, so a handler reads as: * * const gate = await guard(request); * if ('response' in gate) return gate.response; */ export async function guard( request: Request, ): Promise<{ caller: Caller } | { response: Response }> { const session = await auth(); const decision = authorize(session?.user?.role, request.method); if (!decision.ok) { return { response: Response.json({ error: decision.error }, { status: decision.status }) }; } return { caller: { // The subject is the e-mail address, which is what the audit trail records. email: session?.user?.email ?? 'inconnu', role: session?.user?.role ?? 'viewer', }, }; }