# @suaveplan/api-auth-jwt — concrete JWT session resolver for @suaveplan/api-auth. Verifies a JWT presented on the Authorization header (or a configurable cookie) using @suaveplan/jws against either a JWKS endpoint (TTL + negative-cache + in-flight dedup) or a single static JsonWebKey; projects validated claims onto a Principal via an overridable mapper. Returns null on parse/verify failure (chain semantics) and contributes a Bearer JWT OpenAPI security scheme. Stage-4 concrete `SessionResolver` for the [`@suaveplan/api`](../api/README.md) plugin system. Verifies a JWT presented on the `Authorization: Bearer ` header (or a configured cookie) against either a remote JWKS endpoint or a single static JWK, then projects the validated claims onto the foundation's `Principal` contract via an overridable mapper. This is the adapter you pair with hosted identity providers (Clerk, Auth0, Cognito, Stytch, custom Keycloak deployments) or with any self-hosted JWT-issuing provider. It is **session-resolver only** — login / register / refresh routes are the responsibility of an `auth` provider, not this package. ## Documentation tree - [claims-mapper](./src/claims-mapper/claims-mapper.md) — Default and runtime helpers for converting a verified JWT payload (`Claims`) into a `Principal` from `@suaveplan/api-ty… - [claims-mapper](./src/claims-mapper/claims-mapper.md) — Default and runtime helpers for converting a verified JWT payload (`Claims`) into a `Principal` from `@suaveplan/api-ty… - [error-codes](./src/error-codes/error-codes.md) — Stable error-code registry for `@suaveplan/api-auth-jwt`, exposing the `AUTH_JWT.*` namespace via `defineCodes` from `@… - [error-codes](./src/error-codes/error-codes.md) — Stable error-code registry for `@suaveplan/api-auth-jwt`, exposing the `AUTH_JWT.*` namespace via `defineCodes` from `@… - [jwks-cache](./src/jwks-cache/jwks-cache.md) — In-process JWKS cache for `@suaveplan/api-auth-jwt`. Fetches a JWKS document from a remote endpoint, holds it under a p… - [jwks-cache](./src/jwks-cache/jwks-cache.md) — In-process JWKS cache for `@suaveplan/api-auth-jwt`. Fetches a JWKS document from a remote endpoint, holds it under a p… - [options](./src/options/options.md) — Defines the Zod-validated boundary for `createJwtSessionResolver` and the companion `validateOptions` helper. The bound… - [options](./src/options/options.md) — Defines the Zod-validated boundary for `createJwtSessionResolver` and the companion `validateOptions` helper. The bound… - [resolver](./src/resolver/resolver.md) — Stage-4 concrete `SessionResolver` factory for the API plugin system. Reads a JWT from the request (Authorization h… - [resolver](./src/resolver/resolver.md) — Stage-4 concrete `SessionResolver` factory for the API plugin system. Reads a JWT from the request (Authorization h… - [security-scheme](./src/security-scheme/security-scheme.md) — Builds the `SecuritySchemeContribution` this adapter contributes to the generated OpenAPI document. The shape is the ca… - [security-scheme](./src/security-scheme/security-scheme.md) — Builds the `SecuritySchemeContribution` this adapter contributes to the generated OpenAPI document. The shape is the ca… ## Surface - [`./resolver`](src/resolver/resolver.md) — `createJwtSessionResolver(opts)`. - [`./jwks-cache`](src/jwks-cache/jwks-cache.md) — `JwksCache` with TTL, negative cache, in-flight dedup, `kid`-miss refresh. - [`./claims-mapper`](src/claims-mapper/claims-mapper.md) — `defaultClaimsToPrincipal` + `runClaimsMapper` helpers. - [`./security-scheme`](src/security-scheme/security-scheme.md) — `JWT_BEARER_SCHEME` and `createJwtBearerSecurityScheme(name)` for OpenAPI emission. - [`./error-codes`](src/error-codes/error-codes.md) — `AUTH_JWT_*` codes + `AuthJwtError` class. - [`./options`](src/options/options.md) — Zod-validated options boundary. ## Quick start ```ts import { createJwtSessionResolver } from "@suaveplan/api-auth-jwt/resolver"; import { JWT_BEARER_SCHEME } from "@suaveplan/api-auth-jwt/security-scheme"; const resolver = createJwtSessionResolver({ jwksUrl: "https://idp.example/.well-known/jwks.json", issuer: "https://idp.example", audience: "https://api.example", algorithms: ["RS256"], }); // Use in an api-host AuthAdapter: const adapter = { resolver, securityScheme: JWT_BEARER_SCHEME, }; ``` ## Pairing with hosted IdPs The package is designed to be paired with an external identity provider that already issues JWTs. The recipe is the same for every provider — plug in the provider's JWKS URL, issuer, and audience, then add a custom `claimsToPrincipal` when the provider uses non-standard claim names. ### Clerk ```ts createJwtSessionResolver({ jwksUrl: "https://your-instance.clerk.accounts.dev/.well-known/jwks.json", issuer: "https://your-instance.clerk.accounts.dev", audience: "your-frontend-api", algorithms: ["RS256"], claimsToPrincipal: (claims) => { const sub = claims["sub"]; if (typeof sub !== "string") return null; return { id: sub as never, scopes: [] as never[], tenantId: typeof claims["org_id"] === "string" ? (claims["org_id"] as never) : undefined, claims, }; }, }); ``` ### Auth0 ```ts createJwtSessionResolver({ jwksUrl: "https://your-tenant.auth0.com/.well-known/jwks.json", issuer: "https://your-tenant.auth0.com/", audience: "https://api.example.com", algorithms: ["RS256"], }); ``` ### Cognito ```ts createJwtSessionResolver({ jwksUrl: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXX/.well-known/jwks.json", issuer: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXX", audience: "your-cognito-app-client-id", algorithms: ["RS256"], }); ``` ## Semantics `resolve()` returns `null` on any per-request soft failure (missing token, malformed token, bad signature, wrong issuer, wrong audience, expired, not-yet-valid, no matching JWKS key). That collapse lets the upstream `SessionResolverChain` fall through to the next adapter (anonymous, static-bearer, etc.). It throws only on infrastructure failures — `AUTH_JWT.JWKS_FETCH_FAILED` when the JWKS endpoint is unreachable, `AUTH_JWT.INVALID_KEY_FORMAT` for a malformed static key, `AUTH_JWT.INVALID_CLAIMS_MAPPER` when the caller's mapper throws or returns a non-Principal value. ## License MIT — see the repository.