Authentication
Authentication
Section titled “Authentication”Credentials are per-route, not in the input pred.
auth | Behavior |
|---|---|
'none' | No token required — handler gets ctx: undefined unless the client sent a Bearer token and you wired authenticate |
'bearer' (default) | Missing or invalid token → 401 UNAUTHORIZED before the handler runs |
Any route with auth: 'bearer' requires authenticate on the spec — spec throws at load time if it is missing.
import type {Authenticate} from 'callspec';
export type Ctx = {userId: string};
export const authenticate: Authenticate<Ctx> = async (token, req) => { const session = await verifySession(token, req); if (!session) return undefined; return {userId: session.userId};};import {route} from 'callspec';import {predicates as p} from 'runtyp';import type {Ctx} from '../auth';
export const getProfile = route({ output: p.object({userId: p.string()}), meta: {summary: 'Get profile', tags: ['users']}, auth: 'bearer', handler: async (_input, ctx: Ctx) => ({userId: ctx.userId}),});Client
Section titled “Client”Pass the token on every call:
const api = new ApiClient({ baseUrl: 'http://127.0.0.1:3000/v1', headers: () => ({Authorization: `Bearer ${getSessionToken()}`}),});Docs and MCP
Section titled “Docs and MCP”Set meta.authHint. OpenAPI Bearer security is derived from route auth automatically.
scope: 'private' is documented when this mount uses visibility: 'all'. It does not change the auth gate. See Auth and scope.
For richer context from headers and JWT claims, see Request context.