136 lines
6.4 KiB
Markdown
136 lines
6.4 KiB
Markdown
# @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 <jwt>` 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.
|
|
|
|
<!-- begin:auto-docs -->
|
|
## 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…
|
|
<!-- end:auto-docs -->
|
|
|
|
## 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.
|
|
|