Learn authentication by building it right.
Live Demo Β· Threat Model Β· Auth Flows Β· ADRs
Demo note: The login endpoint is rate-limited and protected by adaptive PoW challenges. Repeated failures return increasing proof-of-work difficulty before
429 Too Many Requests.
A from-scratch authentication reference implementation for Cloudflare Workers β PBKDF2 password hashing, JWT dual-token sessions, constant-time comparison, sliding expiration, and a removable observability plugin β all wired together with Hono, Turso (with optional Valkey/Redis caching), and strict TypeScript.
Every design choice traces back to a standard: NIST SP 800-63B for credentials, NIST SP 800-132 for key derivation, OWASP ASVS for verification, and RFC 8725 for JWT best practices.
Shipping a product? Use Better Auth instead β it covers OAuth, passkeys, MFA, rate limiting, and more out of the box with an active plugin ecosystem. This repo exists to teach you how auth works, not to replace a production library.
- Read the code, not just the docs β every security property (timing-safe rejection, session-linked revocation, algorithm pinning) is implemented and tested, not just described
- NIST + OWASP + RFC references throughout β learn the why behind each decision
- 630+ tests including attack-vector suites (token tampering, algorithm confusion, unicode edge cases)
- Observability plugin β structured audit logging, adaptive PoW challenges, agent-authenticated ops API, and WebSocket event streaming β bolts on via Hono middleware without modifying core auth
- Built for the edge β runs on Cloudflare Workers with Web Crypto API, no Node.js dependencies
- Apache-2.0 β fork it, teach with it, learn from it
| Layer | What it does |
|---|---|
| Password storage | PBKDF2-SHA384 with 128-bit salts, integrity digest, version tracking (password-service.ts) |
| Session management | Server-side sessions with device tracking, sliding expiration, max-3-per-user enforcement; optional cache-backed sessions via Valkey/Redis (session-service.ts, cached-session-service.ts) |
| Password change | Current-password re-verification, full PBKDF2 rehash, atomic revocation of all sessions (account-service.ts, ADR-004) |
| JWT dual-token pattern | 15-min access + 7-day refresh tokens, session-linked for revocation (token-service.ts) |
| Auth middleware | Automatic refresh flow, explicit HS256 pinning, typ claim validation (require-auth.ts) |
| Secure cookies | HttpOnly, Secure, SameSite=Strict, Path=/ (cookie.ts) |
| Security headers | HSTS, CSP, CORP/COEP/COOP, Permissions-Policy, fingerprint removal (security.ts) |
| Input validation | Zod schemas with NIST-compliant password policy (length only, no complexity rules) |
| Rate limiting | Fixed-window throttling against brute-force and credential-stuffing attacks: IP-keyed on public auth routes (e.g. login), user-keyed on protected actions; no hard lockouts (NIST-aligned) (ADR-006) |
| Observability plugin | Structured security events, adaptive PoW challenges, agent-authenticated /ops API β plugs in via middleware, removable by deleting one package (ADR-008) |
| CLI tooling | Go TUI (plctl) for querying events, managing sessions, and provisioning agent credentials via the /ops surface (tools/cli/) |
| Attack-vector tests | JWT tampering, algorithm confusion, type confusion, unicode edge cases, info-disclosure checks |
This project intentionally omits features that are outside its educational scope. If you're extending this code toward production (or evaluating what a production auth system requires), the tables below organize the gaps by priority tier.
For most real-world projects, use Better Auth instead of building these yourself.
| Feature | Why It Matters | Standard / Reference |
|---|---|---|
| Breached-password checking | Prevents use of passwords known to be in public breach dumps | NIST SP 800-63B Β§5.1.1.2, HIBP API |
| Feature | Why It Matters | Standard / Reference |
|---|---|---|
| CSRF protection (if SameSite relaxed) | SameSite=Strict currently prevents CSRF; if changed to Lax for UX, an explicit token is needed | OWASP CSRF Cheat Sheet |
| Refresh token rotation | Detects token theft β if a rotated-out refresh token is replayed, revoke the entire session family | RFC 6819 Β§5.2.2.3 |
aud claim in JWTs |
Prevents token from one service being accepted by another sharing the same secret | RFC 7519 Β§4.1.3, RFC 8725 Β§3.9 |
| CSP nonces for inline scripts | Current CSP uses 'unsafe-inline'; nonces eliminate inline-script XSS vectors |
MDN CSP script-src |
| Feature | Why It Matters | Standard / Reference |
|---|---|---|
| TOTP multi-factor auth | Adds a second factor for high-value accounts | RFC 6238, NIST SP 800-63B Β§5.1.4 |
| WebAuthn / passkeys | Phishing-resistant authentication using platform authenticators | WebAuthn Level 2 |
| OAuth / social login | Reduces friction, avoids password fatigue | RFC 6749 |
| Magic links / OTP | Passwordless option for low-risk flows | NIST SP 800-63B Β§5.1.3 |
| Session analytics | Device tracking, concurrent-session visibility, anomaly detection | OWASP Session Management Cheat Sheet |
| Signing key rotation | Allows periodic secret rotation without invalidating all sessions | RFC 7517 (JWK) |
| Feature | Why It Matters | Standard / Reference |
|---|---|---|
| DPoP / token binding | Binds tokens to the client's TLS connection, preventing exfiltration replay | RFC 9449 (DPoP) |
| Multi-tenancy | Isolates user pools, secrets, and policies per tenant | Application-specific |
| Geo-fencing / IP reputation | Blocks logins from unexpected regions or known-bad IPs | OWASP ASVS v5.0 Β§6.3.5 |
| Adaptive authentication | Steps up auth requirements based on risk signals (device, location, behavior) | NIST SP 800-63B Β§6 |
| PBKDF2 iteration upgrade or Argon2id | OWASP recommends 210,000 PBKDF2-SHA512 iterations (Cloudflare limits to 100k); Argon2id is memory-hard | OWASP Password Storage Cheat Sheet |
All of these are excellent reasons to reach for Better Auth instead.
.
βββ apps/
β βββ cloudflare-workers/ # Example Worker + Hono routes
βββ packages/
β βββ core/ # Auth services, middleware, crypto utilities
β βββ infrastructure/ # DB client + utilities
β βββ observability/ # Event emission, adaptive challenges, ops API (removable plugin)
β βββ schemas/ # Zod schemas
β βββ types/ # Shared TypeScript types
βββ tools/
β βββ cli/ # plctl β Go TUI for the /ops surface
βββ docs/
βββ adr/ # Architecture Decision Records
βββ audits/ # Security audits
git clone https://github.com/vhscom/private-landing.git
cd private-landing
bun install
bun run devThat's it β no accounts, no API keys, no .env files. The dev server starts with a local SQLite database and generated secrets. Open http://localhost:8788 to register an account and explore the auth flows.
Have a Turso account? Drop a
.dev.varsfile inapps/cloudflare-workers/(see.dev.vars.example) andbun run devwill automatically use wrangler with your remote database instead. Usebun run dev:localto force the local server regardless.
See CONTRIBUTING.md for testing and deployment instructions.
This repository includes a CLAUDE.md file that provides context for AI assistants. When using Claude Code, Cursor, or similar AI-powered development tools:
- The AI will automatically read
CLAUDE.mdfor project context - Architecture Decision Records in
docs/adr/explain design choices - Security audits in
docs/audits/document the security posture - Tests demonstrate expected behavior and edge cases
The codebase is designed to be AI-readable with clear module boundaries, comprehensive types, and descriptive naming.