Authentication

NextAuth.js v5 with OAuth providers (Google, GitHub) and passwordless magic links. All auth routes and protected pages are pre-configured.


How it works

  1. User visits /login
  2. Chooses OAuth provider or enters email for magic link
  3. After verification, redirected to /app/dashboard
  4. Session stored in database with secure HTTP-only cookies

Key files

  • lib/auth.ts - NextAuth configuration
  • lib/auth/helpers.ts - requireUser(), getCurrentUser()
  • lib/auth/magic-link.ts - Token generation, hashing, verification
  • app/login/page.tsx - Sign-in page
  • app/api/auth/[...nextauth]/route.ts - NextAuth API route
  • app/api/auth/magic-link/request/route.ts - Magic link request endpoint
  • app/auth/magic-link/verify/route.ts - Magic link verification
  • components/magic-link-form.tsx - Email input form
  • emails/MagicLinkSignInEmail.tsx - Magic link email template

Environment variables

AUTH_SECRET=<openssl rand -base64 32>

# OAuth (optional if using magic links only)
AUTH_GOOGLE_ID=xxx.apps.googleusercontent.com
AUTH_GOOGLE_SECRET=GOCSPX-xxx
AUTH_GITHUB_ID=Iv1.xxx
AUTH_GITHUB_SECRET=xxx

# Magic link settings (optional, have defaults)
AUTH_MAGIC_LINK_TOKEN_TTL_MINUTES=15
AUTH_MAGIC_LINK_RATE_LIMIT_PER_HOUR=5

Protecting pages

All pages under app/app/ are automatically protected. The layout calls requireUser().

app/app/projects/page.tsx
// app/app/projects/page.tsx
import { requireUser } from "@/lib/auth/helpers";

export default async function ProjectsPage() {
  const user = await requireUser();
  return <div>Welcome, {user.email}</div>;
}

  • Tokens are 32 bytes of cryptographic randomness
  • Only SHA-256 hashes stored in database
  • Single-use and time-limited (15 min default)
  • Rate limited: 5 requests/hour per email and IP
  • No user enumeration (always returns success)

Adding a new OAuth provider

  1. Add credentials to lib/auth.ts providers array
  2. Add environment variables
  3. Update login page UI if needed

Related: Email for customizing magic link emails