first commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# `session-adapter` — composite `AuthAdapter`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`AuthAdapter` is the composite shape every concrete session-auth adapter
|
||||
exports — pairing the per-request `SessionResolver` with the OpenAPI
|
||||
`SecuritySchemeContribution` and the optional adapter-level CSRF override.
|
||||
`validateAuthAdapter()` is the boundary helper the host calls before stitching
|
||||
an adapter into its resolver chain; it throws `AUTH.SECURITY_SCHEME_MISSING`
|
||||
when the contribution is absent or malformed.
|
||||
|
||||
This package owns the marriage of "what does the credential look like on the
|
||||
wire" (security scheme) and "how do we turn it into a Principal" (resolver) so
|
||||
that downstream consumers — `api-host`, `api-openapi`, and the host's CSRF
|
||||
middleware — read a single source-of-truth shape instead of three loosely
|
||||
coupled fields scattered across every concrete adapter package.
|
||||
|
||||
## Features
|
||||
|
||||
- `csrf` is optional, intentionally: an unset slot defers to the
|
||||
`CsrfPolicy.requiresCsrf(scheme)` default, while a set value pins per-adapter
|
||||
intent (auditable in the host's adapter table).
|
||||
- `validateAuthAdapter` is identity on the happy path so it composes with
|
||||
registration pipelines that need to forward the validated value.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { type AuthAdapter, validateAuthAdapter } from "@suaveplan/api-auth/session-adapter";
|
||||
import type { SessionResolver } from "@suaveplan/api-auth/resolver";
|
||||
|
||||
declare const jwtResolver: SessionResolver;
|
||||
|
||||
const adapter: AuthAdapter = {
|
||||
resolver: jwtResolver,
|
||||
securityScheme: {
|
||||
name: "bearerAuth",
|
||||
scheme: { type: "http", scheme: "bearer", bearerFormat: "JWT" },
|
||||
},
|
||||
};
|
||||
|
||||
validateAuthAdapter(adapter);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Register multiple adapters at host startup; the validator centralises the
|
||||
fail-fast and the host stitches the resolvers into a `SessionResolverChain`:
|
||||
|
||||
```ts
|
||||
import { SessionResolverChain } from "@suaveplan/api-auth/chain";
|
||||
import { type AuthAdapter, validateAuthAdapter } from "@suaveplan/api-auth/session-adapter";
|
||||
|
||||
function buildChain(adapters: readonly AuthAdapter[]): SessionResolverChain {
|
||||
const validated = adapters.map((a) => validateAuthAdapter(a));
|
||||
return new SessionResolverChain(validated.map((a) => a.resolver));
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Symbol | Kind | Description |
|
||||
|-----------------------|-------------|---------------------------------------------------------------|
|
||||
| `AuthAdapter` | interface | `{ resolver, securityScheme, csrf? }`. |
|
||||
| `validateAuthAdapter` | function | Boundary validator; throws `AUTH.SECURITY_SCHEME_MISSING`. |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `csrf` is a `boolean | undefined` field rather than a richer `CsrfDecision`
|
||||
record. The host's middleware combines this slot with the per-route override
|
||||
and the `CsrfPolicy.requiresCsrf` default into a final decision — the
|
||||
adapter does NOT need three-valued logic for the same intent.
|
||||
- `validateAuthAdapter` returns its argument unchanged so call sites can write
|
||||
`const a = validateAuthAdapter(buildAdapter(opts));` without re-binding.
|
||||
- The structural type is `interface` (not `type`) so concrete adapter packages
|
||||
MAY use declaration merging to attach extension fields without forking the
|
||||
contract.
|
||||
Reference in New Issue
Block a user