Files
skills/docs/api-stack/api/api-auth-static-principal/src/factory/create-static-principal-resolver.md
2026-05-29 08:20:56 +01:00

3.8 KiB

factory/create-static-principal-resolver — fixed-Principal resolver

(no submodules)

Purpose

Constructs a SessionResolver from @suaveplan/api-auth that returns the configured Principal verbatim on every request — no token decoding, no JWKS, no I/O. This is the workhorse adapter for plugin integration tests, the api-plugin-example fixture, and any environment where the goal is to exercise the downstream pipeline (RBAC gate, tenant resolver chain reading principal.tenantId, tenant-scoped DB) with a deterministic identity carried in config.

The factory consumes options previously validated by the options module so construction is total: every misuse maps to either AUTH_STATIC_PRINCIPAL.INVALID_OPTIONS or AUTH_STATIC_PRINCIPAL.INVALID_PRINCIPAL and surfaces synchronously at construction time. The resolver itself never throws at request time.

Features

  • Defensive clone on every resolve() — callers cannot mutate one returned Principal and observe the change on subsequent calls.
  • applyTo(req) predicate for arbitrary "return null for this request" rules.
  • nullForPaths suffix-glob list (/webhooks/*) and exact-match patterns for the most common public-route test setup.
  • Idempotent no-op dispose() matching the SessionResolver contract.
  • Returned objects are frozen (top-level + scopes + claims) to make attempted mutations fail loudly in strict mode.

Usage: Basic

import { createStaticPrincipalResolver } from "@suaveplan/api-auth-static-principal/resolver";

const resolver = createStaticPrincipalResolver({
    principal: {
        id: "u1",
        scopes: ["billing:write"],
        tenantId: "acme",
        claims: { sub: "u1" },
    },
});

const principal = await resolver.resolve(
    new Request("https://example.test/api/billing/invoices")
);
// principal.id === "u1"

Usage: Advanced

Combine applyTo and nullForPaths to model a mixed-route test surface:

import { createStaticPrincipalResolver } from "@suaveplan/api-auth-static-principal/resolver";

const resolver = createStaticPrincipalResolver({
    principal: {
        id: "u1",
        scopes: ["billing:write", "billing:read"],
        tenantId: "acme",
        claims: { sub: "u1" },
    },
    nullForPaths: ["/webhooks/*", "/healthz"],
    applyTo: (req) => !req.headers.has("x-skip-auth"),
});

// /healthz and /webhooks/stripe resolve to null.
// Requests with the `x-skip-auth` header resolve to null.
// Everything else returns the configured Principal.

API Reference

Symbol Kind Description
createStaticPrincipalResolver(o) function Returns a SessionResolver carrying o.principal on every match.

Implementation Notes

The clone strategy is: deep-freeze a single template at construction, then clone-from-template per resolve(). The per-call clone is unavoidable — otherwise two callers each freezing the returned object would freeze each other's state. The per-call clone copies the top level plus scopes (array spread) and claims (object spread); deeper structures inside claims are not cloned. This matches the Principal contract: claims values are unknown, so the package can't responsibly deep-clone them. Callers passing mutable nested claims values are responsible for not mutating them in-place.

pathMatches accepts only exact-match and a single suffix glob (/foo/*) on purpose — richer matching belongs in applyTo. The trailing slash is preserved when stripping the * so /foo/* matches /foo/x but not /foox. The pattern dialect is documented on the spec and exercised by the test file.