Files
2026-05-27 23:29:53 +01:00

78 lines
3.5 KiB
Markdown

<!-- markdownlint-disable MD024 -->
# Capability Spec Delta: api-auth-static-principal (api-auth-static-principal)
**Package**: `@suaveplan/api-auth-static-principal`
**Status**: SHIPPED
## ADDED Requirements
### Requirement: resolveSession returns the configured Principal verbatim
The package SHALL return the exact `Principal` from `options.principal` on every `resolveSession()` call, regardless of request shape. The returned Principal MUST deep-equal the configured one (no mutation, no reference-leak across calls).
#### Scenario: every request resolves to the configured Principal
- **WHEN** the adapter is configured with `principal: { id: "u1", scopes: ["x:write"], tenantId: "acme" }` AND two distinct requests are processed
- **THEN** both `resolveSession()` calls return Principals that deep-equal the configured one
#### Scenario: returned Principal is not the same reference each call
- **WHEN** two `resolveSession()` calls are made
- **THEN** the returned objects are deep-equal AND callers cannot mutate one and affect the other (defensive clone OR documented frozen reference — pin one)
**Verified by:** `src/factory/create-static-principal-resolver.test.ts::"every request resolves to the configured Principal"` AND `src/factory/create-static-principal-resolver.test.ts::"returned Principal is isolated across calls"`
---
### Requirement: nullForPaths overrides specific routes
When `options.nullForPaths` is set, `resolveSession()` SHALL return `null` for requests whose path matches any entry (exact match or glob). Used to test `publicRoutes` behaviour without changing adapter wiring.
#### Scenario: matching path returns null
- **WHEN** `nullForPaths: ["/webhooks/*"]` is configured AND the request path is `/webhooks/stripe`
- **THEN** `resolveSession()` returns `null`
#### Scenario: non-matching path returns the Principal
- **WHEN** `nullForPaths: ["/webhooks/*"]` is configured AND the request path is `/billing/invoices`
- **THEN** `resolveSession()` returns the configured Principal
**Verified by:** `src/factory/create-static-principal-resolver.test.ts::"matching path in nullForPaths returns null"` AND `src/factory/create-static-principal-resolver.test.ts::"non-matching path returns the configured Principal"`
---
### Requirement: Conforms to AuthSession contract
#### Scenario: conformance suite passes
- **WHEN** `runAuthSessionConformance("static-principal-direct", () => createStaticPrincipalResolver({ principal: P }))` runs
- **THEN** every invariant passes
**Verified by:** `src/factory/create-static-principal-resolver.spec-conformance.test.ts::"static-principal-direct conforms to AuthSession contract"`
---
### Requirement: Options Zod-validated
#### Scenario: missing principal.id throws
- **WHEN** `createStaticPrincipalResolver({ principal: { scopes: [], claims: {} } as never })`
- **THEN** a `SuaveplanError` with code `AUTH_STATIC_PRINCIPAL_INVALID_OPTIONS` is thrown synchronously
**Verified by:** `src/options/static-principal-resolver-options.test.ts::"throws when principal.id is missing"`
---
### Requirement: Registered in `api-host/testing` adapter table
#### Scenario: createTestHost resolves the kind sentinel
- **WHEN** `createTestHost({ adapters: { "auth.session": { kind: "static-principal", principal: P } } })`
- **THEN** the resulting `host.auth` is an instance produced by this factory
**Verified by:** `tests/integration/integration.test.ts::"createTestHost resolves kind:\"static-principal\" to this adapter"`
<!-- markdownlint-enable MD024 -->