first commit
This commit is contained in:
155
docs/api-stack/api/api-auth-bearer-static/README.md
Normal file
155
docs/api-stack/api/api-auth-bearer-static/README.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# @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`.
|
||||
|
||||
Reference in New Issue
Block a user