UNPKG

@beignet/core

Version:

Core framework primitives for Beignet

123 lines 4.36 kB
import type { InferOutput, StandardSchema } from "../../contracts/index.js"; import type { HttpRequestLike, RouteHook } from "../types.js"; type MaybePromise<T> = T | Promise<T>; /** * Arguments passed to auth route-hook callbacks. */ export type AuthHookArgs<Ctx, HeadersSchema extends StandardSchema | undefined = undefined> = { /** * Framework-neutral request. */ req: HttpRequestLike; /** * Current route handler context. */ ctx: Ctx; /** * Matched contract metadata and schemas. */ contract: { metadata?: Record<string, unknown>; }; /** * Parsed path parameters. */ path: unknown; /** * Parsed query parameters. */ query: unknown; /** * Hook-owned request headers. * * When the auth hooks declare a `headers` schema, this is that schema's * output parsed from the raw lowercase request header record. Without a * schema it is the raw lowercase header record itself, so auth resolution * never depends on each route's contract header schema. */ headers: HeadersSchema extends StandardSchema ? InferOutput<HeadersSchema> : Record<string, string>; /** * Parsed request body. */ body: unknown; }; /** * Options for route-scoped auth hooks. * * Auth additions must not include `gate`: the server re-attaches the gate * declared by the context blueprint after every hook, so elevated identities * are authorized against the updated context automatically. */ export type AuthHooksOptions<Ctx, AddedCtx extends object & { gate?: never; }, HeadersSchema extends StandardSchema | undefined = undefined> = { /** * Hook name prefix used in diagnostics. */ name?: string; /** * Optional Standard Schema for the credential headers this hook reads. * * The schema is validated against the raw lowercase request header record * before `resolve` runs. On `required()` hooks a schema failure rejects the * request with a framework-owned 401; on `optional()` hooks a schema failure * skips auth resolution; `public()` hooks never parse headers. */ headers?: HeadersSchema; /** * Resolve authenticated context additions for the current request. * * Return `null` when the request is unauthenticated. Required hooks will * reject that request; optional hooks will add no auth context. */ resolve: (args: AuthHookArgs<Ctx, HeadersSchema>) => MaybePromise<AddedCtx | null>; }; /** * Route-scoped auth hook set. */ export type AuthRouteHooks<Ctx, AddedCtx extends object & { gate?: never; }> = { /** * Mark a route as intentionally public. */ public: () => RouteHook<Ctx, Record<string, never>>; /** * Resolve auth when present and add optional auth fields to the handler ctx. */ optional: () => RouteHook<Ctx, Partial<AddedCtx>>; /** * Require auth and add authenticated fields to the handler ctx. */ required: () => RouteHook<Ctx, AddedCtx>; }; /** * Create route-scoped authentication hooks. * * The outer call binds the app context; the inner call takes auth options and * infers the added context from `resolve`: * * ```ts * const auth = createAuthHooks<AppContext>()({ * resolve: ({ ctx }) => (ctx.auth ? { user: ctx.auth.user } : null), * }); * ``` * * Use `auth.required()` on routes that require an authenticated actor and * `auth.optional()` where handlers can use auth when present. The returned * route hooks enrich handler `ctx`; business authorization still belongs in * feature policies or use cases. * * Declare a `headers` schema when credentials live in request headers. The * hook validates the raw lowercase header record itself, so `resolve` receives * typed headers without contract casts and a `required()` hook rejects * missing or malformed credentials with a framework-owned 401. * * @returns A function that takes auth options and returns public, optional, * and required route-hook factories. */ export declare function createAuthHooks<Ctx>(): <AddedCtx extends object & { gate?: never; }, HeadersSchema extends StandardSchema | undefined = undefined>(options: AuthHooksOptions<Ctx, AddedCtx, HeadersSchema>) => AuthRouteHooks<Ctx, AddedCtx>; export {}; //# sourceMappingURL=auth.d.ts.map