Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
# `@suaveplan/api-auth-jwt` — JWT session resolver
**Package**: `@suaveplan/api-auth-jwt`
**Status**: PROPOSED
**Type**: new-package
**Stage**: 4 (Base adapters) — see umbrella `openspec/changes/api-plugin-foundation/`
## Why
Per design doc §2.3, auth is split into two distinct contracts: session resolution (`Request → Principal | null`) and account lifecycle (provider). `api-auth-jwt` implements only the session-resolver half — it verifies a JWT from an `Authorization: Bearer` header or cookie against a JWKS URL or static key using `@suaveplan/jws`, and projects the resulting claims onto a `Principal`. It is NOT a provider: no login/register/refresh routes. This is what you pair with hosted IdPs (Clerk, Auth0, Cognito) or with any self-hosted JWT-issuing provider.
Per Stage 4: ships both a direct callable surface and a worker-proxy surface (§5.2a). `runAuthSessionConformance` passes against both.
## What changes
Net-new package depending on `@suaveplan/api-auth` (contract), `@suaveplan/api-types`, `@suaveplan/api-worker-rpc`, `@suaveplan/jws`, `@suaveplan/error`, and `zod`.
- ADDED: `createJwtSessionResolver(opts)` — direct-surface factory returning an `AuthAdapter` (session-resolver only).
- ADDED: `createJwtSessionResolverWorkerProxy(port, opts)` — proxy-surface factory.
- ADDED: `JwtSessionResolverOptions` — Zod schema (`jwksUrl?: string`, `staticKey?: JsonWebKey`, `algorithms: string[]`, `audience?: string | string[]`, `issuer?: string | string[]`, `clockToleranceSec?: number`, `cookieName?: string`, `headerName?: string`, `claimToPrincipal?: (claims) => Principal`).
- ADDED: `defaultClaimToPrincipal` — projects `sub`/`scope`/`scopes`/`tenant_id`/`tid` onto a `Principal`.
- ADDED: `securitySchemeContribution()` — returns OpenAPI `securitySchemes` block describing the Bearer JWT scheme (consumed by `api-openapi` Stage 8).
- ADDED: `AUTH_JWT_*` error codes (`AUTH_JWT_MISSING_TOKEN`, `AUTH_JWT_INVALID_SIGNATURE`, `AUTH_JWT_EXPIRED`, `AUTH_JWT_WRONG_AUDIENCE`, `AUTH_JWT_WRONG_ISSUER`, `AUTH_JWT_ALGORITHM_NOT_ALLOWED`, `AUTH_JWT_JWKS_FETCH_FAILED`, `AUTH_JWT_INVALID_OPTIONS`).
- ADDED: JWKS caching with TTL + ETag revalidation; refresh on `kid` miss.
## Out of scope
- Account lifecycle (register/login/logout/refresh) — that's `auth.provider`, not this package. See `api-auth-better-auth` (Stage 6) and `api-auth-rolled` (Stage 8).
- Cookie-based CSRF gating — the host's request pipeline (§6) triggers `Bun.CSRF` based on auth-adapter declaration; this package only declares whether cookie auth is in use.
- Multi-resolver chaining — handled by `api-auth` core (Stage 1).
## Source mapping
Genesis-original. Source of design intent: `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md`.
| Requirement | Design doc section |
|---|---|
| Session resolver, not provider | §2.3 |
| JWKS + static-key support | §2.3 (`api-auth-jwt` bullet) |
| Worker-proxy variant | §5.2a |
| Conformance both surfaces | §13.4 |
| OpenAPI securityScheme contribution | §7 |
## Verification approach
- `src/factory/create-jwt-session-resolver.test.ts` — direct-surface tests against real `@suaveplan/jws`-signed tokens.
- `src/factory/create-jwt-session-resolver.spec-conformance.test.ts` — `runAuthSessionConformance("jwt-direct", ...)`.
- `src/proxy/create-jwt-session-resolver-worker-proxy.test.ts` — proxy unit tests over `MessageChannel`.
- `src/proxy/create-jwt-session-resolver-worker-proxy.spec-conformance.test.ts` — `runAuthSessionConformance("jwt-worker-proxy", ...)`.
- `src/jwks/jwks-cache.test.ts` — TTL, ETag, `kid` miss refresh.
- `src/claim-mapping/default-claim-to-principal.test.ts` — claim projection, scope normalization, tenant extraction.
- `tests/integration/integration.test.ts` — sign with `@suaveplan/jws`, verify through this resolver end-to-end.
## Migration impact
None — additive only.