import type { Middleware, RequestContext } from '@venturekit/runtime'; /** * Dynamic role → scopes lookup. Structurally identical to * `@venturekit/auth`'s `RoleScopesLookup` (the `vk_role_scopes`-backed * resolver) — declared locally so this package carries no dependency * on the auth package; any `(role) => scopes` function fits. */ export type RoleScopesLookup = (role: string) => Promise | readonly string[]; /** * Minimal structural requirement on the app's tenant-user row: the * per-tenant role that keys into `scopesByRole`. Apps extend it with * their own columns (id, status, timestamps, …) and get them back, * typed, from {@link getTenantUser}. */ export interface TenantUserWithRole { role: string; } /** * Loads the caller's tenant-user row for the CURRENT tenant. Called * only when both `ctx.user` and `ctx.tenant` are set. Return `null` * for "no row" — never throw for the not-found case. */ export interface TenantUserResolver { (ctx: RequestContext): Promise | M | null; } export interface TenantUserScopesMiddlewareOptions { /** * Tenant-user lookup for `(ctx.user, ctx.tenant)` — the store * fallback for claim misses, and the lazy row loader behind * {@link getTenantUser} / {@link requireTenantUser}. */ tenantUser: TenantUserResolver; /** * JWT claim carrying the packed per-tenant roles (see * ./tenant-roles-claim.ts for the format and the write-path * contract). Defaults to {@link TENANT_ROLES_CLAIM} * (`custom:tenantRoles`) — harmless for apps that never write the * attribute, since an absent claim always falls back to * {@link TenantUserScopesMiddlewareOptions.tenantUser}. Pass * `false` to force the store lookup on every request (instant * revocation at one query per privileged request). */ rolesClaim?: string | false; /** * The role → scopes matrix of the app — either a static table or a * (possibly async) lookup function. Roles that resolve to nothing * grant nothing (fail-closed). Tiered role models should build * cumulative sets so higher roles are supersets of lower ones. * * For a DB-backed matrix (mutable at runtime, manageable from an * admin UI) pass `@venturekit/auth`'s * `createRoleScopesResolver(...).lookup` — the mapping lives in * `vk_role_scopes` (structure shipped by the auth package's * migrations, rows seeded by the app). */ scopesByRole: Record | RoleScopesLookup; /** * Gate evaluated before any role scope is granted — the hook for * app-specific "row exists but is not in good standing" rules * (pending verification, active suspension, unpaid seat, …). * The tenant-user row is still cached for {@link getTenantUser} so * handlers can inspect WHY the caller is inactive. * * Default: every resolved row is active. */ isActive?: (tenantUser: M) => boolean; /** * Tenant-INDEPENDENT extra scopes for the authenticated user — * evaluated even when no tenant resolved. Use for cross-tenant * operator allowlists (platform admins) or per-user scope * overrides. Keep it fail-closed: return `[]` on any doubt. */ userScopes?: (ctx: RequestContext) => Promise | readonly string[]; } /** * Build the middleware. See the module header for placement and * semantics. */ export declare function createTenantUserScopesMiddleware(options: TenantUserScopesMiddlewareOptions): Middleware; /** * The caller's tenant-user row in the current tenant, or `null` when * there is none (or the middleware didn't run for this request). * * Lazy: the claims fast path grants scopes without loading the row, * so the first call here triggers the middleware's `tenantUser` * resolver; the result — including `null` — is cached on the request * context and shared with concurrent callers. */ export declare function getTenantUser(ctx: RequestContext): Promise; /** * Like {@link getTenantUser} but throws `ForbiddenError` (403) when * no tenant-user row resolves. For handlers whose scope gate already * implies a row exists (any role-granted scope does), a miss here * means either a wiring bug (middleware absent from the route's * `middleware: [...]` array) or a stale packed claim for an * assignment that was just revoked — in both cases 403 is the right * answer. */ export declare function requireTenantUser(ctx: RequestContext): Promise; //# sourceMappingURL=tenant-user-scopes-middleware.d.ts.map