first commit
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# `composition`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Top-level factory `createRolledAuthAdapter` that wires the four
|
||||
sub-modules (`token-rotator`, `session`, `principal-projection`,
|
||||
`security-schemes`) into a single `AuthAdapter` ready for the host to
|
||||
register. Keeping the composition explicit (rather than letting each
|
||||
sub-module own its own host wiring) means the dependency graph is one
|
||||
arrow: options flow in, an adapter object flows out, no hidden state.
|
||||
|
||||
The returned value extends the `AuthAdapter` contract (`resolver` +
|
||||
`securityScheme`) with three additional surfaces the future provider
|
||||
half (`api-auth-rolled-provider`) will consume: `issueToken`,
|
||||
`verifyToken`, and the rotator handle so operators can wire
|
||||
`setKeys(...)` from an out-of-band rotation cron.
|
||||
|
||||
## Features
|
||||
|
||||
- Single entry point — `createRolledAuthAdapter(rawOptions)`.
|
||||
- Options validated via the package's Zod schema before any wiring runs.
|
||||
- Defaults filled at the composition layer (cookie name, rotation
|
||||
period, session TTL) so individual sub-modules stay focused on their
|
||||
one concern.
|
||||
- Returned adapter is structurally a valid `AuthAdapter` so any host
|
||||
that accepts the contract will accept the rolled adapter.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createRolledAuthAdapter } from "@suaveplan/api-auth-rolled";
|
||||
|
||||
const adapter = createRolledAuthAdapter({
|
||||
store,
|
||||
keys: [
|
||||
{ kid: "k1", privateJwk, publicJwk, active: true },
|
||||
],
|
||||
issuer: "https://api.example.com",
|
||||
audience: "example-front",
|
||||
});
|
||||
|
||||
host.registerAuthSession(adapter);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { createRolledAuthAdapter } from "@suaveplan/api-auth-rolled";
|
||||
|
||||
const adapter = createRolledAuthAdapter({
|
||||
store,
|
||||
keys: initialKeys,
|
||||
issuer: "https://api.example.com",
|
||||
audience: "example-front",
|
||||
cookieName: "acme.session",
|
||||
sessionTtlSec: 1800,
|
||||
rotationPeriodMs: 12 * 60 * 60 * 1000,
|
||||
});
|
||||
|
||||
// Operator rotation cron — fetch new key from KMS, swap atomically.
|
||||
setInterval(async () => {
|
||||
if (!adapter.rotator.decide().shouldRotate) return;
|
||||
const next = await kms.nextKeyset();
|
||||
adapter.rotator.setKeys(next);
|
||||
}, 60_000);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `createRolledAuthAdapter(rawOptions: unknown): RolledAuthAdapter`
|
||||
- `RolledAuthAdapter extends AuthAdapter`: adds `id: "rolled"`, `rotator`,
|
||||
`issueToken(userId)`, `verifyToken(token)`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The adapter intentionally keeps `id` as the literal `"rolled"` so a host
|
||||
running both Better-Auth and rolled can disambiguate by adapter id. The
|
||||
`securityScheme` slot points at the bearer contribution (the canonical
|
||||
single-scheme shape) but the `.schemes` map on the contribution exposes
|
||||
both the cookie and bearer entries — emitters aware of dual-transport
|
||||
adapters can register both names without a separate adapter property.
|
||||
Reference in New Issue
Block a user