156 lines
8.3 KiB
Markdown
156 lines
8.3 KiB
Markdown
# @suaveplan/api-auth-bearer-static — SessionResolver adapter for service-to-service static bearer tokens. Matches Authorization: Bearer <token> 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 <token>` 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`.
|
|
|
|
<!-- begin:auto-docs -->
|
|
## 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` —…
|
|
<!-- end:auto-docs -->
|
|
|
|
## 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`.
|
|
|