Capstone: Production-Ready Auth
What we built
Starting from a working auth system, we added every feature a production app needs:
| Feature | What it does | Key files |
|---|---|---|
| Email verification | Proves the user owns the email | verification.ts, signup route |
| Session tracking | Records device, IP, last active per session | device-sessions.ts |
| Session management | List, revoke, sign out everywhere | sessions-mgmt.ts routes |
| Session security | Idle/absolute timeout, rotation, new-IP detection | session-security.ts |
| Step-up auth | Re-authenticate before sensitive actions | step-up.ts |
| Account deletion | Soft delete, grace period, hard delete, cleanup | deletion.ts routes, deletion-job.ts |
| SAML/SSO | Enterprise login via IdP | saml.ts, saml.ts routes |
| JIT provisioning | Auto-create accounts on first SSO login | jit-provisioning.ts |
The complete user lifecycle
Signup
│
├─ Verification email sent
│ └─ User clicks link → email_verified = 1
│
├─ Login (password, magic link, passkey, or SSO)
│ ├─ Device session created (IP, user agent, device name)
│ └─ 2FA check if enabled
│
├─ Active use
│ ├─ Session last_active_at updated
│ ├─ /me/sessions shows all devices
│ └─ New-IP detection alerts user
│
├─ Sensitive action (change email, disable 2FA, create API key)
│ ├─ requireRecentAuth → 403 if not recent
│ ├─ POST /auth/confirm (password or TOTP)
│ └─ Action proceeds within 5-minute window
│
├─ Enterprise SSO (alternative entry)
│ ├─ Domain lookup → SAML provider
│ ├─ Redirect to IdP → assertion → callback
│ └─ JIT provisioning (create user, map groups to roles)
│
└─ Deletion
├─ Step-up required
├─ 30-day grace period (cancellable)
├─ Hard delete cascades through all tables
└─ Shared data anonymized, audit logs kept Project structure
src/
app.ts # setup(), all routes, onRequest hooks
server.ts # starts the server
db.ts # schema with all tables
auth.ts # authenticate, has2FA, hasPasskeys, requireVerified
sessions.ts # session store with pending, lastAuthAt
cookies.ts # cookie helpers
verification.ts # email verification tokens
device-sessions.ts # device tracking per session
session-security.ts # timeout checks, new-IP detection
step-up.ts # requireRecentAuth
saml.ts # SAML SP and IdP helpers
jit-provisioning.ts # auto-create users on SSO login
deletion-job.ts # hard delete after grace period
routes/
auth.ts # signup (with verification), login
verification.ts # verify-email, resend
sessions-mgmt.ts # list, revoke, revoke-all
step-up.ts # POST /auth/confirm
deletion.ts # request, cancel, status
saml.ts # SAML login and callback Test the complete system
npm run dev
# === Email verification ===
curl -c cookies.txt -X POST http://localhost:3000/signup \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"pass12345","name":"Test User"}'
# Check console for verification URL
curl "http://localhost:3000/verify-email?token=TOKEN"
# === Session management ===
# Log in from two "devices"
curl -c cookies1.txt -X POST http://localhost:3000/login \
-H "Content-Type: application/json" \
-H "User-Agent: Chrome/120 macOS" \
-d '{"email":"[email protected]","password":"pass12345"}'
curl -c cookies2.txt -X POST http://localhost:3000/login \
-H "Content-Type: application/json" \
-H "User-Agent: Safari/17 iPhone" \
-d '{"email":"[email protected]","password":"pass12345"}'
# List sessions — should show both
curl -b cookies1.txt http://localhost:3000/me/sessions
# Revoke the iPhone session
curl -b cookies1.txt -X DELETE http://localhost:3000/me/sessions/SESSION_ID
# === Step-up auth ===
# Try changing email without re-auth
curl -b cookies1.txt -X PUT http://localhost:3000/me/email \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}'
# 403: Re-authentication required
# Re-authenticate
curl -b cookies1.txt -X POST http://localhost:3000/auth/confirm \
-H "Content-Type: application/json" \
-d '{"password":"pass12345"}'
# Now change email
curl -b cookies1.txt -X PUT http://localhost:3000/me/email \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}'
# 200: Email updated
# === Account deletion ===
curl -b cookies1.txt -X POST http://localhost:3000/auth/confirm \
-H "Content-Type: application/json" \
-d '{"password":"pass12345"}'
curl -b cookies1.txt -X POST http://localhost:3000/me/delete
# Scheduled for 30 days
curl -b cookies1.txt http://localhost:3000/me/delete/status
# { "scheduled": true, "daysRemaining": 30 }
curl -b cookies1.txt -X POST http://localhost:3000/me/delete/cancel
# Cancelled What you built across all seven courses
This is the seventh course in the series. Together, they cover the complete auth and security stack:
| Course | What it covers |
|---|---|
| Authentication | Passwords, sessions, cookies, JWTs, basic roles |
| OAuth and Social Login | GitHub/Google login, account linking |
| Securing Your API | Rate limiting, CSRF, token rotation, password reset, headers |
| Web Security Fundamentals | SQL injection, XSS, SSRF, IDOR, path traversal |
| Authorization | RBAC, permissions, multi-tenancy, API keys, policy functions |
| 2FA and Passwordless | TOTP, recovery codes, magic links, passkeys |
| Production Auth Patterns | Email verification, sessions, step-up, deletion, SAML |
From a blank project to a production-ready auth system with enterprise SSO — every piece built with plain functions on Hectoday HTTP.
Challenges
Challenge 1: Add SCIM provisioning. Build a SCIM 2.0 endpoint that accepts user create/update/delete events from the IdP. When an employee is removed from the IdP, automatically deactivate their account in your app.
Challenge 2: Add login history. Store every login (successful and failed) with timestamp, IP, device, and method used (password, passkey, magic link, SSO). Show the last 10 logins on the security settings page.
Challenge 3: Add email change confirmation. Before changing to a new email, send a confirmation link to the new address. Only update the email after the link is clicked. This prevents changing to an email the user does not control.
Challenge 4: Add session fingerprinting. Beyond IP and user agent, track browser fingerprint attributes (screen size, timezone, language). Flag sessions where the fingerprint changes mid-session (possible session hijacking).
What is the correct order of auth checks for a sensitive route like 'change email'?
Which feature from this course would have prevented the most real-world account takeovers?