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>
4.5 KiB
@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.
import { createJwtTenantResolver } from "@suaveplan/api-tenant-resolver-jwt";
const resolver = createJwtTenantResolver();
const ctx = await resolver.resolve(request);
// → { id: "acme", slug: "acme" } | null
Documentation tree
- error-codes — Defines the stable error-code registry for
@suaveplan/api-tenant-resolver-jwt. Every public failure path in the resol…- error-codes — Defines the stable error-code registry for
@suaveplan/api-tenant-resolver-jwt. Every public failure path in the resol…
- error-codes — Defines the stable error-code registry for
- options — Defines and validates the
JwtTenantResolverOptionsaccepted bycreateJwtTenantResolver. The options shape is delibe…- options — Defines and validates the
JwtTenantResolverOptionsaccepted bycreateJwtTenantResolver. The options shape is delibe…
- options — Defines and validates the
- resolver — Implements The tenant resolver factory
createJwtTenantResolver. The resolver reads the tenant identifier from…- resolver — Implements The tenant resolver factory
createJwtTenantResolver. The resolver reads the tenant identifier from…
- resolver — Implements The tenant resolver factory
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.