Authentication
What this layer solves
Most apps have pages anyone can see (public) and pages that require logging in (protected/authenticated). Authentication answers the question: “Who is this user?” Authorization answers: “What are they allowed to do?”
Public vs protected pages
| Type | Example | What happens |
|---|---|---|
| Public | Landing page, pricing, blog, docs | Anyone can visit — no login required |
| Protected | Dashboard, settings, billing, admin | User must be logged in; redirected to sign-in if not |
In a Next.js app, you enforce this with middleware (checks the session before rendering) or server component logic (redirect if no user). The pattern is the same regardless of auth provider.
How authentication works (simplified)
- User clicks Sign in → gets sent to the auth provider (Google, GitHub, email link, etc.)
- Provider verifies identity → sends back a token (usually a JWT or session cookie)
- Your app stores that token → checks it on every protected request
- On Sign out, the token is destroyed
OAuth is the protocol that lets users sign in with Google, GitHub, Apple, etc. instead of creating a new password. Most auth services handle the OAuth flow for you.
Options
| Service | Best for | Tradeoffs |
|---|---|---|
| Clerk | Drop-in UI components, great DX, fast setup | Paid past free tier; vendor dependency |
| Supabase Auth | Already using Supabase for database | Tighter coupling to Supabase ecosystem |
| Neon Auth | Already using Neon for Postgres | Newer; growing feature set |
| NextAuth / Auth.js | Maximum control, self-hosted session logic | More setup; you manage more of the flow |
| Firebase Auth | Google ecosystem, mobile-first | Different data model than SQL-based stacks |
Clerk for most projects — fast to set up, beautiful pre-built components, and you can swap later if needed. If you’re already on Supabase, use Supabase Auth to keep everything in one place.
Outline: adding auth to a Next.js app (Clerk example)
- Install the Clerk SDK —
npm install @clerk/nextjs - Add env vars —
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYandCLERK_SECRET_KEYfrom your Clerk dashboard - Wrap your app with
<ClerkProvider>inlayout.tsx - Add middleware —
clerkMiddleware()inmiddleware.tsto protect routes - Public routes — explicitly list which paths skip auth (e.g.
/,/pricing,/blog/*) - Sign in/up pages — use Clerk’s
<SignIn />and<SignUp />components or build custom UI - Access user —
auth()in server components,useUser()in client components
Key concepts to understand
Session — a record that the user is currently logged in. Can be stored as a cookie, JWT, or server-side session.
JWT (JSON Web Token) — a compact, signed token containing user info. Common in modern auth; your app decodes it to know who’s making the request.
OAuth provider — Google, GitHub, Apple, etc. — they verify identity so you don’t have to handle passwords.
Row Level Security (RLS) — database-level rules that control which rows a user can read/write. Used heavily with Supabase. See Security basics.
Protected API routes — your API endpoints should also check auth, not just the frontend pages. Never trust the client alone.
Official links
When to pick an alternative
- Auth.js if you want full control and don’t mind wiring the UI yourself.
- Supabase Auth if your database is already Supabase and you want fewer moving parts.
- Firebase Auth if you’re in Google’s ecosystem and using Firestore.
Last reviewed: April 2026.