Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
# @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.
|
||||
|
||||
Reference in New Issue
Block a user