shipsafe-auth
v0.1.0
Published
Passwordless auth toolkit for ShipSafe projects — durable rate limiting, in-tab OTP + click-approve, and a pluggable step-up assurance ladder (passkey, FaceGate). Framework-agnostic core; routes/migration shipped via scaffold.
Maintainers
Readme
shipsafe-auth
Passwordless auth toolkit for ShipSafe projects. Framework-agnostic core plus a scaffold that drops the Next.js App Router routes and Supabase migration into a target project.
Status:
0.0.0— core + scaffold + FaceGate adapter built and unit-proven against the dist; not yet published, and not yet wired into a live app (the consolidation step). See the status table below.
Why this exists
Several ShipSafe projects each reimplemented passwordless auth, and the
implementations drifted — most visibly in rate limiting (some lanes are durable
and table-backed, some are best-effort in-memory Maps that don't survive
serverless cold starts). This package extracts the hardened ShipSafe version
once so projects converge on it via a version bump instead of hand-porting.
Shape
| Layer | Ships as | Status |
| --- | --- | --- |
| Rate-limit enforcer + storage-adapter interface | npm core | ✅ built + proven |
| Validators + OTP / match-code logic + leak-safe issuer | npm core | ✅ built + proven |
| StepUpChallenger/StepUpVerifier seam + passkey provider | npm core | ✅ built + proven |
| OtpSlots React primitive | shipsafe-auth/react entry | ✅ built + proven |
| Email-sender interface + Resend adapter | npm core | ✅ built + proven |
| App Router routes + Supabase migration + helpers | npx shipsafe-auth add scaffold | ✅ built + proven (generation) |
| FaceGate human-presence adapter | shipsafe-auth/facegate entry, implements the seam | ✅ built + proven; verifier enforces action-binding (canonical hash match) |
| ShipSafe app wired at the package | src/lib/auth/rate-limit.ts re-exports the package | ✅ app's own 11 rate-limit tests pass against it; whole-app typecheck clean |
| Package vitest suite | pnpm test | ✅ 15 tests (rate-limit + validators + approve-token + match-code + FaceGate binding) |
| npm publish | [email protected] | ⬜ dry-run verified (55 files, auth OK); actual publish awaits explicit go (irreversible/public) |
| Full cutover (delete shim, import package directly everywhere) | — | ⬜ optional cosmetic step; shim already routes the app through the package |
Assurance ladder
The step-up tier is a pluggable ladder, not a single method:
- Tier 0 — identity: email OTP + click-approve (match code). AAL1.
- Tier 1 — possession: passkey step-up. AAL2, phishing-resistant.
- Tier 2 — presence: FaceGate liveness — a live human is here for this action. Optional adapter; only projects authorizing agent/high-stakes actions need it.
Build
pnpm build # tsc -> dist, then fix-esm-extensions (.js + Node ESM smoke test)
pnpm typecheckSource imports stay extensionless (Turbopack consumes src directly via a
tsconfig path alias); the build adds .js to dist for plain-Node ESM
consumers. Do not hand-add extensions in source.
