# @suaveplan/api-auth-bearer-static — SessionResolver adapter for service-to-service static bearer tokens. Matches Authorization: Bearer against a fixed token→Principal table using constant-time comparison from @suaveplan/crypto-utils; unknown tokens defer to the next chain entry. Supports an optional per-token mutual-TLS gate that rejects when the host did not surface request.tls.peerCertificate. Ships both a direct surface (createBearerStaticResolver) and a worker-proxy surface (createBearerStaticResolverWorkerProxy) over @suaveplan/api-worker-rpc; both pass runAuthSessionConformance from @suaveplan/api-auth/testing. OpenAPI security-scheme contribution (HTTP Bearer). Zod-validated options, AUTH_BEARER_STATIC.* error codes, OTel match counter partitioned by outcome. Stage-4 service-to-service `SessionResolver` adapter for the `@suaveplan/api` plugin system. Matches an inbound `Authorization: Bearer ` header against a fixed `token → Principal` table using constant-time comparison from `@suaveplan/crypto-utils`. Unknown tokens return `null` so the host's `SessionResolverChain` falls through to the next adapter (typically `api-auth-jwt` for user sessions). Ships both the direct surface (`createBearerStaticResolver`) and the worker-proxy surface (`createBearerStaticResolverWorkerProxy` + `serveBearerStaticResolverOnPort`) over `@suaveplan/api-worker-rpc`; both pass `runAuthSessionConformance` from `@suaveplan/api-auth/testing`. ## Documentation tree - [constant-time](./src/constant-time/token-match.md) — `findTokenIndex` performs a constant-time lookup of a presented bearer token against a fixed table, returning the match… - [token-match](./src/constant-time/token-match.md) — `findTokenIndex` performs a constant-time lookup of a presented bearer token against a fixed table, returning the match… - [error-codes](./src/error-codes/error-codes.md) — The error-codes module defines the stable `AUTH_BEARER_STATIC.*` namespace that every public failure path in this packa… - [error-codes](./src/error-codes/error-codes.md) — The error-codes module defines the stable `AUTH_BEARER_STATIC.*` namespace that every public failure path in this packa… - [factory](./src/factory/create-bearer-static-resolver.md) — `createBearerStaticResolver` is the direct-surface entry point of the package. It accepts validated `BearerStaticResolv… - [create-bearer-static-resolver](./src/factory/create-bearer-static-resolver.md) — `createBearerStaticResolver` is the direct-surface entry point of the package. It accepts validated `BearerStaticResolv… - [mtls](./src/mtls/mtls-gate.md) — The mTLS gate enforces an optional second factor on individual token table entries. When operators mark an entry with `… - [mtls-gate](./src/mtls/mtls-gate.md) — The mTLS gate enforces an optional second factor on individual token table entries. When operators mark an entry with `… - [options](./src/options/bearer-static-resolver-options.md) — The options module owns the public construction surface of every resolver in this package. It defines the `BearerStatic… - [bearer-static-resolver-options](./src/options/bearer-static-resolver-options.md) — The options module owns the public construction surface of every resolver in this package. It defines the `BearerStatic… - [proxy](./src/proxy/create-bearer-static-resolver-worker-proxy.md) — The worker-proxy surface is the second of the two Stage-4 surfaces this adapter ships (`§5.2a`). It lets the host insta… - [create-bearer-static-resolver-worker-proxy](./src/proxy/create-bearer-static-resolver-worker-proxy.md) — The worker-proxy surface is the second of the two Stage-4 surfaces this adapter ships (`§5.2a`). It lets the host insta… - [security-scheme](./src/security-scheme/security-scheme-contribution.md) — This module builds the OpenAPI 3.1 `components.securitySchemes` contribution the adapter publishes to the host's `AuthA… - [security-scheme-contribution](./src/security-scheme/security-scheme-contribution.md) — This module builds the OpenAPI 3.1 `components.securitySchemes` contribution the adapter publishes to the host's `AuthA… - [telemetry](./src/telemetry/instruments.md) — The telemetry module owns the single OpenTelemetry counter this package emits — `api.auth.bearer_static_match_total` —… - [instruments](./src/telemetry/instruments.md) — The telemetry module owns the single OpenTelemetry counter this package emits — `api.auth.bearer_static_match_total` —… ## Install ```sh bun add @suaveplan/api-auth-bearer-static ``` ## Direct surface ```ts import { createBearerStaticResolver } from "@suaveplan/api-auth-bearer-static"; import { SessionResolverChain } from "@suaveplan/api-auth"; const serviceAuth = createBearerStaticResolver({ tokens: [ { token: process.env.CRON_TOKEN, principal: { id: "svc_cron", scopes: ["jobs:run"], claims: { kind: "service" }, }, }, { token: process.env.ADMIN_TOKEN, principal: { id: "svc_admin", scopes: ["admin:*"], claims: { kind: "admin" }, }, mtlsRequired: true, }, ], }); // Chained — service tokens first, JWT users fall through. const chain = new SessionResolverChain([serviceAuth, jwtResolver]); ``` ## Chained-resolver pattern with `api-auth-jwt` The intended deployment topology pairs this adapter with `@suaveplan/api-auth-jwt` for user sessions. Static-token callers match first; everything else flows to JWT. The chain's `first-match-wins` semantics means unknown tokens never throw — they simply move on. ```ts import { SessionResolverChain } from "@suaveplan/api-auth"; import { createBearerStaticResolver } from "@suaveplan/api-auth-bearer-static"; import { createJwtSessionResolver } from "@suaveplan/api-auth-jwt"; // future sibling const chain = new SessionResolverChain([ createBearerStaticResolver({ tokens: [/* service callers */], }), createJwtSessionResolver({ issuer: "https://idp.example", audience: "suaveplan", }), ]); ``` ## Worker-proxy surface Use the worker-proxy variant when the host process should not hold the real token table — e.g. an isolated auth worker serving multiple host workers. The host imports `createBearerStaticResolverWorkerProxy(port)`; the worker imports `serveBearerStaticResolverOnPort(port, options)`. The proxy transparently passes `runAuthSessionConformance`, so plugin code treats it identically to the direct surface. ```ts // host.ts const proxy = createBearerStaticResolverWorkerProxy(port); const chain = new SessionResolverChain([proxy, jwtResolver]); ``` ```ts // worker.ts serveBearerStaticResolverOnPort(port, { tokens: [/* real table — never crosses to the host */], }); ``` ## Optional mutual-TLS gate Token entries MAY carry `mtlsRequired: true`. When set, the resolver requires the host listener to surface a verified peer certificate via `request.tls.peerCertificate`; absent → returns `null` (outcome: `mtls-required`). The adapter does NOT establish TLS — that is the host listener's responsibility (Bun TLS, Node `https`, Cloudflare Worker TLS terminator, etc.). ## Token rotation This adapter is intentionally static — rotate tokens by reloading host config. There is no DB-backed lookup, no JWKS, no refresh loop. If you need rotation without restarts, use `api-auth-better-auth` or a future `api-auth-token-table` adapter; this one is the simplest possible thing that resolves a fixed table against constant-time match. ## Errors The package exposes the `AUTH_BEARER_STATIC.*` code namespace via `AUTH_BEARER_STATIC_CODES`. Only `INVALID_OPTIONS` is thrown (synchronously, at factory construction); `NO_TOKEN`, `UNKNOWN_TOKEN`, and `MTLS_REQUIRED` are surfaced via the OTel match counter's `outcome` label. ## Telemetry One counter — `api.auth.bearer_static_match_total{outcome=match|no-token|unknown-token|mtls-required}` — increments exactly once per `resolve()` call. ## Conformance Both surfaces pass `runAuthSessionConformance` from `@suaveplan/api-auth`. Spec-conformance tests live alongside the source under `src/factory/*.spec-conformance.test.ts` and `src/proxy/*.spec-conformance.test.ts`.