/** * Request schema for the native OIDC token exchange * (`POST /api/auth/oidc/native/token`). Single-sourced here so the runtime * `safeParse` and the OpenAPI registry (`src/lib/openapi/routes/auth.ts`) * describe the identical wire contract. */ import { z } from "zod/v4"; /** * `{ code, codeVerifier }`. * * - `code` is the opaque handoff code from the `healthlog://oidc-callback?code=` * redirect: the `hlh_` prefix + the 43-char base64url body of a 256-bit * CSPRNG value. Pinned exactly so only well-formed codes reach the hash * lookup. * - `codeVerifier` is the app's PKCE verifier (RFC 7636 ยง4.1 โ€” 43โ€“128 chars of * the unreserved set); the server checks `S256(codeVerifier)` against the * challenge bound at mint (constant-time) before consuming the code. */ export const oidcNativeTokenSchema = z .object({ code: z .string() .regex(/^hlh_[A-Za-z0-9_-]{43}$/, "Malformed handoff code.") .describe( "One-time handoff code from the healthlog://oidc-callback?code= redirect.", ), codeVerifier: z .string() .regex( /^[A-Za-z0-9\-._~]{43,128}$/, "The PKCE code_verifier must be 43โ€“128 chars of the RFC 7636 unreserved set.", ) .describe("The app's PKCE code_verifier (RFC 7636)."), }) .meta({ id: "OidcNativeTokenRequest", description: "Exchange a one-time native OIDC handoff code (+ its PKCE verifier) for the standard native token bundle.", }); export type OidcNativeTokenRequest = z.infer;