@beignet/core
Version:
Core framework primitives for Beignet
123 lines • 4.36 kB
TypeScript
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