import { verifyCsrf, type Context } from "@wrnexus/core"; import { createAuthHttpHandlers } from "../http/index.ts"; import { getDefaultAuthEngine, getDefaultAuthRouteOptions, getDefaultAuthSchemas, hasDefaultAuthEngine, } from "../runtime.ts"; export type AuthHttpHandlers = ReturnType; import { AUTH_ROUTE_DEFINITIONS, type AuthHandlerName } from "./definitions.ts"; const ROUTES = Object.fromEntries( AUTH_ROUTE_DEFINITIONS.map((definition) => [definition.path, definition]), ) as Readonly>; function unavailable(): Response { return Response.json( { ok: false, error: "WRN-AUTH-NOT-CONFIGURED", message: "Configure auth.engine before serving package auth routes.", }, { status: 503, headers: { "cache-control": "no-store" } }, ); } function normalizeRoutePath(value: string): string { try { return (decodeURIComponent(value).replace(/\/+$/, "") || "/").toLowerCase(); } catch { return (value.replace(/\/+$/, "") || "/").toLowerCase(); } } function requestRoutePath(ctx: Context): string { const matchedRoute = ctx.locals.__wrnexusRoute; if (typeof matchedRoute === "string" && matchedRoute.startsWith("/api/auth/")) { return normalizeRoutePath(matchedRoute); } try { return normalizeRoutePath(new URL(ctx.req.url).pathname); } catch { return normalizeRoutePath(ctx.url.pathname); } } function methodNotAllowed(allowed: readonly string[]): Response { return Response.json( { ok: false, error: "Method Not Allowed" }, { status: 405, headers: { allow: allowed.join(", "), "cache-control": "no-store" } }, ); } function handlersFor(ctx: Context): AuthHttpHandlers | undefined { if (!hasDefaultAuthEngine()) return undefined; const routeOptions = getDefaultAuthRouteOptions(); return createAuthHttpHandlers({ engine: getDefaultAuthEngine(), schemas: getDefaultAuthSchemas(), baseUrl: routeOptions.baseUrl ?? ctx.url.origin, passkey: routeOptions.passkey, onSignedIn: routeOptions.onSignedIn, onSignedOut: routeOptions.onSignedOut, }); } /** Invoke one concrete handler. Route-specific entry modules use this path. */ export async function invokeAuthHandler(name: AuthHandlerName, ctx: Context): Promise { const handlers = handlersFor(ctx); if (!handlers) return unavailable(); const method = ctx.req.method.toUpperCase(); const unsafeMethod = method !== "GET" && method !== "HEAD" && method !== "OPTIONS"; if (unsafeMethod && getDefaultAuthRouteOptions().csrf !== false && !verifyCsrf(ctx)) { return Response.json( { ok: false, error: "Invalid CSRF token" }, { status: 403, headers: { "cache-control": "no-store" } }, ); } try { return await handlers[name](ctx); } catch (error) { const message = error instanceof Error ? error.message : String(error); if (message.startsWith("WRN-AUTH-PASSKEY-PROVIDER:")) { return Response.json( { ok: false, error: "Passkeys are unavailable", code: "passkey-provider-not-configured", }, { status: 503, headers: { "cache-control": "no-store" } }, ); } console.error(`[wrnexus:auth] ${name} failed`, error); return Response.json( { ok: false, error: "Authentication request failed" }, { status: 500, headers: { "cache-control": "no-store" } }, ); } } /** Backward-compatible dispatcher for applications importing the shared route. */ export async function dispatchAuthRoute(routePath: string, ctx: Context): Promise { const definition = ROUTES[normalizeRoutePath(routePath)]; if (!definition) { return Response.json( { ok: false, error: "Not Found" }, { status: 404, headers: { "cache-control": "no-store" } }, ); } const method = ctx.req.method.toUpperCase(); if (!definition.methods.some((allowed) => allowed === method)) { return methodNotAllowed(definition.methods); } return invokeAuthHandler(definition.handler, ctx); } export default async function authApi(ctx: Context): Promise { return dispatchAuthRoute(requestRoutePath(ctx), ctx); } export const GET = authApi; export const POST = authApi;