Initial Commit
This commit is contained in:
73
packages/api-stack/api/api-tenant-resolver-jwt/README.md
Normal file
73
packages/api-stack/api/api-tenant-resolver-jwt/README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# @suaveplan/api-tenant-resolver-jwt — tenant-resolver adapter for the Suaveplan API plugin system. Reads the already-resolved `Principal.tenantId` from the per-request context (attached by an earlier `api-auth-*` chain) and projects it into a `TenantContext`. When the configured claim is present the resolver short-circuits the chain; when absent it returns `null` so a header/subdomain resolver can take over. Configurable claim key (default `tenantId`) and strict-mode (`requirePrincipal: true`) that throws `TENANT_RESOLVER_JWT.PRINCIPAL_MISSING_TENANT_ID` instead of advancing.
|
||||
|
||||
Stage-4 tenant resolver for the Suaveplan API plugin system. Reads the
|
||||
already-resolved `Principal.tenantId` (attached by an earlier `api-auth-*`
|
||||
chain step) and projects it into a `TenantContext` for the host's per-request
|
||||
pipeline.
|
||||
|
||||
```ts
|
||||
import { createJwtTenantResolver } from "@suaveplan/api-tenant-resolver-jwt";
|
||||
|
||||
const resolver = createJwtTenantResolver();
|
||||
const ctx = await resolver.resolve(request);
|
||||
// → { id: "acme", slug: "acme" } | null
|
||||
```
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [error-codes](./src/error-codes/error-codes.md) — Defines the stable error-code registry for `@suaveplan/api-tenant-resolver-jwt`. Every public failure path in the resol…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — Defines the stable error-code registry for `@suaveplan/api-tenant-resolver-jwt`. Every public failure path in the resol…
|
||||
- [options](./src/options/options.md) — Defines and validates the `JwtTenantResolverOptions` accepted by `createJwtTenantResolver`. The options shape is delibe…
|
||||
- [options](./src/options/options.md) — Defines and validates the `JwtTenantResolverOptions` accepted by `createJwtTenantResolver`. The options shape is delibe…
|
||||
- [resolver](./src/resolver/resolver.md) — Implements The tenant resolver factory `createJwtTenantResolver`. The resolver reads the tenant identifier from…
|
||||
- [resolver](./src/resolver/resolver.md) — Implements The tenant resolver factory `createJwtTenantResolver`. The resolver reads the tenant identifier from…
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Why a dedicated package
|
||||
|
||||
The tenant-resolver chain (design doc §6, step 4) runs after the session
|
||||
resolver. When the session is a JWT carrying `tenant_id` / `tid`, the tenant
|
||||
resolver's job is trivial: read `principal.tenantId`. Collapsing that into a
|
||||
dedicated adapter — rather than encoding it inside `api-auth-jwt` — keeps the
|
||||
two contracts orthogonal and lets operators chain `tenant-resolver-jwt` with
|
||||
`api-tenant-resolver-header` and `api-tenant-resolver-subdomain` (Stage 8)
|
||||
for the paths a JWT alone doesn't cover.
|
||||
|
||||
## Chain interaction (forward reference)
|
||||
|
||||
The host runs resolvers left-to-right; the first non-`null` result wins:
|
||||
|
||||
```
|
||||
[ api-tenant-resolver-jwt, api-tenant-resolver-header, api-tenant-resolver-subdomain ]
|
||||
| | |
|
||||
reads principal reads X-Tenant-Id reads sub.domain
|
||||
(typical bearer) (admin / S2S calls) (public site routes)
|
||||
```
|
||||
|
||||
When the JWT carries no tenant claim (or the request is unauthenticated on a
|
||||
public route), `tenant-resolver-jwt` returns `null` and the next resolver in
|
||||
the chain runs. With `requirePrincipal: true` the chain stops here and a
|
||||
401-equivalent is surfaced via `TENANT_RESOLVER_JWT.PRINCIPAL_MISSING_TENANT_ID`.
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Default | Effect |
|
||||
| ------------------ | ------------- | ------------------------------------------------------------------ |
|
||||
| `id` | `"jwt"` | Resolver id used in chain logs and metric tags. |
|
||||
| `claimKey` | `"tenantId"` | Reads `Principal.tenantId` directly; any other key reads `claims`. |
|
||||
| `requirePrincipal` | `false` | Throw instead of returning `null` when claim is absent. |
|
||||
| `getPrincipal` | reads `req.principal` | Override how the principal is fetched from the request. |
|
||||
|
||||
## Error codes
|
||||
|
||||
- `TENANT_RESOLVER_JWT.PRINCIPAL_MISSING_TENANT_ID` — strict-mode rejected.
|
||||
- `TENANT_RESOLVER_JWT.INVALID_OPTIONS` — construction-time Zod failure.
|
||||
|
||||
## Exports
|
||||
|
||||
- `.` — top-level barrel re-exporting everything below.
|
||||
- `./resolver` — `createJwtTenantResolver`, `CreateJwtTenantResolverOptions`, `PrincipalExtractor`.
|
||||
- `./options` — `JwtTenantResolverOptions`, `parseJwtTenantResolverOptions`.
|
||||
- `./error-codes` — `JwtTenantResolverError`, `TENANT_RESOLVER_JWT_CODES`, `JwtTenantResolverErrorCode`.
|
||||
|
||||
Reference in New Issue
Block a user