81 lines
3.7 KiB
Markdown
81 lines
3.7 KiB
Markdown
<!-- markdownlint-disable MD024 -->
|
|
|
|
# Capability Spec Delta: api-auth-bearer-static (api-auth-bearer-static)
|
|
|
|
**Package**: `@suaveplan/api-auth-bearer-static`
|
|
**Status**: PROPOSED
|
|
|
|
## ADDED Requirements
|
|
|
|
### Requirement: Static token table resolves to Principal
|
|
|
|
The package SHALL match incoming bearer tokens against a fixed table supplied at construction time. A matching token produces the associated `Principal`; an unknown token produces `null`. Token comparison MUST be constant-time so an attacker cannot probe valid token prefixes via timing.
|
|
|
|
#### Scenario: known token returns its Principal
|
|
|
|
- **WHEN** a request carries `Authorization: Bearer T1` AND the table contains `{ token: "T1", principal: P }`
|
|
- **THEN** `resolveSession()` returns `P`
|
|
|
|
#### Scenario: unknown token returns null
|
|
|
|
- **WHEN** a request carries `Authorization: Bearer NOT_IN_TABLE`
|
|
- **THEN** `resolveSession()` returns `null` AND emits `AUTH_BEARER_STATIC_UNKNOWN_TOKEN` on the failure counter
|
|
|
|
#### Scenario: token match is constant-time
|
|
|
|
- **WHEN** two unknown tokens of equal length are compared against the table
|
|
- **THEN** the comparison loop runs to completion for both (no early exit on first mismatched byte)
|
|
|
|
**Verified by:** `src/factory/create-bearer-static-resolver.test.ts::"known token returns its Principal"` AND `src/factory/create-bearer-static-resolver.test.ts::"unknown token returns null and increments failure counter"` AND `src/constant-time/token-match.test.ts::"comparison runs to completion regardless of mismatch position"`
|
|
|
|
---
|
|
|
|
### Requirement: Optional mutual-TLS gate
|
|
|
|
A token entry MAY carry `mtlsRequired: true`. When set, the resolver MUST require the request to carry a verified peer certificate (surfaced by the host listener); absence MUST produce `null` and emit `AUTH_BEARER_STATIC_MTLS_REQUIRED`.
|
|
|
|
#### Scenario: mTLS-gated token rejects requests without peer cert
|
|
|
|
- **WHEN** an mTLS-gated token is presented but `request.tls.peerCertificate` is undefined
|
|
- **THEN** `resolveSession()` returns `null` AND emits `AUTH_BEARER_STATIC_MTLS_REQUIRED`
|
|
|
|
#### Scenario: mTLS-gated token accepts requests with peer cert
|
|
|
|
- **WHEN** the same token is presented AND `request.tls.peerCertificate` is present
|
|
- **THEN** `resolveSession()` returns the Principal
|
|
|
|
**Verified by:** `src/mtls/mtls-gate.test.ts::"rejects mtls-required token without peer certificate"` AND `src/mtls/mtls-gate.test.ts::"accepts mtls-required token with peer certificate"`
|
|
|
|
---
|
|
|
|
### Requirement: Direct and worker-proxy surfaces both pass conformance
|
|
|
|
The adapter SHALL pass `runAuthSessionConformance` under both surfaces.
|
|
|
|
#### Scenario: direct surface conforms
|
|
|
|
- **WHEN** `runAuthSessionConformance("bearer-static-direct", () => createBearerStaticResolver(...))` runs
|
|
- **THEN** every invariant passes
|
|
|
|
#### Scenario: worker-proxy surface conforms
|
|
|
|
- **WHEN** `runAuthSessionConformance("bearer-static-worker-proxy", ...)` runs over a real `MessageChannel`
|
|
- **THEN** every invariant passes
|
|
|
|
**Verified by:** `src/factory/create-bearer-static-resolver.spec-conformance.test.ts::"bearer-static-direct conforms to AuthSession contract"` AND `src/proxy/create-bearer-static-resolver-worker-proxy.spec-conformance.test.ts::"bearer-static-worker-proxy conforms to AuthSession contract"`
|
|
|
|
---
|
|
|
|
### Requirement: Options Zod-validated
|
|
|
|
`BearerStaticResolverOptions` MUST be Zod-validated. Empty `tokens` array OR a non-string token entry MUST throw `AUTH_BEARER_STATIC_INVALID_OPTIONS` at factory construction.
|
|
|
|
#### Scenario: empty tokens array throws
|
|
|
|
- **WHEN** `createBearerStaticResolver({ tokens: [] })`
|
|
- **THEN** a `SuaveplanError` with code `AUTH_BEARER_STATIC_INVALID_OPTIONS` is thrown synchronously
|
|
|
|
**Verified by:** `src/options/bearer-static-resolver-options.test.ts::"throws on empty tokens array"`
|
|
|
|
<!-- markdownlint-enable MD024 -->
|