Authentication System

Build your own Clerk/Auth0

✓ Good to Vibe

Authentication is a solved problem with excellent open source foundations. Auth.js (NextAuth) handles the complexity of OAuth flows, session management, and token refresh. What you're really building is the UX layer and any custom requirements like organization management or specific MFA flows.

Why This Works

Auth.js handles complexity

OAuth flows, PKCE, token refresh, session management—all handled. You focus on UX.

Cost savings at scale

Clerk charges $0.02/MAU after 10k. At 100k users, that's $1,800/month vs ~$0.

Full customization

Your login pages, your email templates, your verification flows. No "Powered by" badges.

Data control

User data stays in your database. No syncing, no vendor migration complexity.

Compliance simplicity

SOC2, HIPAA, GDPR audits are easier when auth isn't a third-party black box.

Integration depth

Direct database access means complex authorization logic stays simple.

Tech Stack

LayerToolsWhy
Auth FrameworkAuth.js v5 (NextAuth)Handles OAuth complexity, session management, CSRF protection. 50+ providers supported.
DatabasePostgreSQL + PrismaRelational integrity for users, sessions, accounts. Prisma adapter works out of box.
EmailResend or AWS SESTransactional email for verification, password reset. Resend has great DX.
OAuth ProvidersArctic libraryLightweight OAuth client. Better than hand-rolling provider integrations.
Password Hashingbcrypt or Argon2bcrypt is battle-tested. Argon2 is newer, more resistant to GPU attacks.
MFAotplib + QRCodeTOTP generation and verification. Standard protocol, works with any authenticator app.

Architecture

┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Client │────▶│ Auth.js │────▶│ PostgreSQL │ │ (React) │ │ (API) │ │ (Users) │ └─────────────┘ └──────┬──────┘ └─────────────┘ │ ┌────────────────┼────────────────┐ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ OAuth │ │ Email │ │ TOTP │ │ Providers │ │ (Resend) │ │ (MFA) │ └─────────────┘ └─────────────┘ └─────────────┘

The Prompt

Copy this into Cursor, Claude, or ChatGPT to generate a working implementation:

Build a production authentication system with Auth.js v5: ## Database Schema (Prisma) model User { id String @id @default(cuid()) email String @unique emailVerified DateTime? passwordHash String? name String? image String? // MFA mfaEnabled Boolean @default(false) mfaSecret String? backupCodes String[] // Metadata createdAt DateTime @default(now()) updatedAt DateTime @updatedAt lastLoginAt DateTime? accounts Account[] sessions Session[] } model Account { // Standard Auth.js account model for OAuth id String @id @default(cuid()) userId String type String provider String providerAccountId String refresh_token String? access_token String? expires_at Int? token_type String? scope String? id_token String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([provider, providerAccountId]) } model Session { id String @id @default(cuid()) sessionToken String @unique userId String expires DateTime userAgent String? ipAddress String? createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model VerificationToken { identifier String token String @unique expires DateTime type String // 'email' | 'password-reset' | 'magic-link' @@unique([identifier, token]) } ## Auth.js Configuration Configure with these providers and callbacks: - Credentials provider for email/password - Google OAuth with profile mapping - GitHub OAuth with email scope - Magic link via email provider Callbacks: - jwt: Add user.id and roles to token - session: Expose id and roles in session - signIn: Check email verification, rate limit attempts ## API Routes POST /api/auth/register - Validate email format and password strength (min 8 chars, mixed case, number) - Check if email exists (return generic error to prevent enumeration) - Hash password with bcrypt (cost factor 12) - Create user, send verification email - Return success (don't auto-login until verified) POST /api/auth/verify-email - Validate token exists and not expired - Mark user emailVerified - Delete token - Redirect to login with success message POST /api/auth/forgot-password - Always return success (prevent enumeration) - If email exists, create reset token (1 hour expiry) - Send reset email with secure link POST /api/auth/reset-password - Validate token exists and not expired - Validate new password strength - Hash and update password - Invalidate all existing sessions - Delete token, redirect to login POST /api/auth/mfa/enable - Generate TOTP secret - Return QR code for authenticator app - Don't enable until verified POST /api/auth/mfa/verify - Validate TOTP code - If enabling: set mfaEnabled, generate backup codes - If logging in: complete authentication ## Security Checklist - [ ] Rate limit login attempts (5/minute per IP) - [ ] Rate limit password reset (3/hour per email) - [ ] Secure, httpOnly, sameSite cookies - [ ] CSRF protection on all forms - [ ] Password hashing with bcrypt cost 12+ - [ ] Constant-time comparison for tokens - [ ] Session invalidation on password change - [ ] Secure headers (CSP, HSTS, X-Frame-Options)

Timeline

Week 1: Core Auth

  • Set up Auth.js with Prisma adapter
  • Implement email/password registration with validation
  • Build email verification flow with Resend
  • Add password reset functionality
  • Create login/register UI pages

Week 2: OAuth & MFA

  • Configure Google and GitHub OAuth
  • Handle account linking for same email
  • Implement TOTP-based MFA
  • Generate and validate backup codes
  • Build MFA setup wizard UI

Week 3: Hardening

  • Add rate limiting with Upstash
  • Implement session management UI (view/revoke)
  • Add magic link authentication option
  • Security audit and penetration testing
  • Documentation and recovery procedures

Open Source References

Auth.js ↗

The foundation. Handles OAuth, sessions, CSRF. Study the source.

Lucia ↗

Lighter alternative to Auth.js. Good for learning auth internals.

Better Auth ↗

Newer framework with good DX. Worth comparing approaches.

Arctic ↗

Lightweight OAuth client. Use for custom provider integrations.

Cost Comparison

Clerk

$1,800/month
At 100k MAU. Plus premium features.

Self-Hosted

$20/month
Database + email sending. Scales linearly.
$1,780/month
potential annual savings

Watch Out For