4.2 KiB
@suaveplan/api-auth-rolled — rolled session-token resolver (Stage 8.12 keystone)
Package: @suaveplan/api-auth-rolled
Status: PROPOSED
Type: new-package
Stage: 8 — see umbrella openspec/changes/api-plugin-foundation/
Why
Per design doc §2.3, genesis ships two reference auth providers in v1:
Better-Auth (third-party wrapper) AND api-auth-rolled (composes the
genesis crypto/auth primitives end-to-end). Shipping the rolled
session-resolver in Stage 8 is the keystone that proves rotating-key
JWTs compose against the existing primitives without a third-party
dependency, and gives operators a fallback session-auth path that owns
zero third-party runtime dependencies.
This change scopes Stage 8.12 to the session-resolver half of the
dual contract. The matching AuthProviderAdapter (account-lifecycle
routes, password gating, MFA, OAuth, etc.) is deferred to a successor
change api-auth-rolled-provider so this milestone ships at 100%
coverage in a single iteration. Sharing the host's main DB connection,
the primitive composition, and the JWT key-rotation contract are all
established here — the successor change layers routes onto the same
adapter shape.
What changes
- ADDED:
createRolledAuthAdapter({ store, keys, issuer, audience, ... })returnsAuthAdapter(session resolver + security-scheme contribution). - ADDED:
TokenRotator— manages a rotating set of RS256 signing keys. Active key signs new JWTs; retired-but-non-revoked keys still verify in-flight tokens; revoked keys neither.decide()is the advisory rotation timing signal;setKeys()is the operator-driven swap. - ADDED: Session tokens are signed JWTs (RS256) via
@suaveplan/jws+@suaveplan/crypto-rsaJWKs; key rotation supported by JWKS-style key id. The resolver accepts bothAuthorization: Bearer <token>andCookie: suaveplan.session=<token>(configurable name). - ADDED:
RolledAuthStore— the structural storage interface the adapter consumes. Concrete DB-backed implementations are out of scope for this change (operator wires them). - ADDED:
projectUserRowToPrincipal— maps internalUserRowshape to the@suaveplan/api-typesPrincipalcontract. - ADDED:
securitySchemescontribution forapi-openapi— cookie + bearer schemes both contributed. - ADDED: Error codes (
AUTH_ROLLED_*): exhaustive 20-code registry covering account-lifecycle, session, and infrastructure failure modes.
Out of scope (deferred to api-auth-rolled-provider)
/auth/register,/auth/login,/auth/logout,/auth/refreshroute implementations.- Password hashing + strength + breach gating at registration time.
- MFA (TOTP, WebAuthn), SRP, OAuth flows.
- Email-verification / password-reset token issuance.
- Migrations for
users,user_sessions, etc.
The session resolver shipped here verifies tokens issued by any provider
that signs to the same JWK set, so the dual contract is split clean —
operators MAY wire api-auth-rolled for session reads against a separate
provider (e.g. Clerk) that issues the JWTs.
Source mapping
Genesis-original. Source of design intent:
docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md.
| Requirement | Design doc section |
|---|---|
Implements auth.session |
§2.3 |
| Shares the host's main DB connection | §2.3 |
Composes crypto-rsa + jws primitives |
§2.3 |
Rotating-kid JWT signing |
§2.3 |
Verification approach
src/session/session.test.ts— issue, verify (active kid, retired kid, revoked kid), expired token rejected, cookie + bearer extraction.src/token-rotator/token-rotator.test.ts—decide/setKeys/snapshot/publicJwkForpaths.src/principal-projection/principal-projection.test.ts— projection invariants.src/options/options.test.ts— Zod schema + DATABASE_REQUIRED + key-set invariants.src/security-schemes/security-schemes.test.ts— cookie + bearer contributions validate.src/composition/composition.spec-conformance.test.ts— everyAUTH_ROLLED_*code is thrown by at least one production path.
Migration impact
None — additive only. api-host registers this when
host.auth.session = "rolled".