Files
2026-05-27 23:29:53 +01:00

74 lines
4.5 KiB
Markdown

# @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`.