How to Add Authentication to Next.js (2026 Step-by-Step)
There's no single 'right' auth library for Next.js — the choice depends on whether you want batteries-included social login (Clerk), full flexibility with self-host (Auth.js), or maximum simplicity for a focused product (custom JWT). This guide covers all three with working examples and honest tradeoffs.
The three real options for Next.js auth
Clerk — drop-in hosted auth. Pre-built UI components (`<SignIn />`, `<UserButton />`), social login providers configured in their dashboard, generous free tier (10k MAU). Trade-off: another vendor in your stack, less control over the user table, $25/month past the free tier.
Auth.js (formerly NextAuth) — open-source auth library. Bring your own database, configure providers in code, full control. Trade-off: more setup, more concepts to learn, you maintain it. Free forever.
Custom JWT — for products with simple auth needs (email + password, one or two roles). Write a `hashPassword` + `verifyPassword` + `signJWT` + `verifyJWT` helper, set an httpOnly cookie, done. Trade-off: no social login, no magic links, you're rolling your own — including the parts you'll get wrong.
When to use each
Use Clerk if: you want auth working in 30 minutes, you need social login (Google, GitHub, Apple) without configuring OAuth apps yourself, you have non-technical teammates who'll manage user lists from a dashboard, and you don't mind a vendor.
Use Auth.js if: you want zero vendor lock-in, you need uncommon providers (specific SAML, OIDC, identity federation), you want full control over the user table and session storage, and you're comfortable with auth concepts.
Use custom JWT if: your auth is email + password only, you don't anticipate adding social login, you have a single user-types schema, and you're comfortable with the security responsibility. This is what InBuild itself uses.
Clerk — 30 minute setup
Sign up at clerk.com → create application → copy the API keys to `.env.local` as `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY`.
Install `npm i @clerk/nextjs`. Wrap your `app/layout.tsx` with `<ClerkProvider>`. Add `clerkMiddleware()` in `middleware.ts`. Drop `<SignIn />` and `<SignUp />` on your auth pages. Use `auth()` server-side to get the user; `<UserButton />` client-side for the avatar dropdown.
Configure social providers in Clerk's dashboard (no code). Configure email templates in the dashboard. Configure session length in the dashboard. Most of what you'd normally code lives in their UI.
Auth.js — 2 hour setup
Install `npm i next-auth@beta`. Auth.js v5 (current in 2026) supports App Router natively.
Create `auth.ts` at the project root configuring providers (e.g. GitHub: register an OAuth app on GitHub, paste client ID + secret into env vars). Export `handlers`, `auth`, `signIn`, `signOut`. Create `app/api/auth/[...nextauth]/route.ts` exporting `handlers.GET` and `handlers.POST`.
Add a Prisma adapter (or another database adapter) so users persist. Auth.js handles the OAuth dance; you handle the user table. Wrap protected pages with `auth()` server-side to check for a session.
Custom JWT — under 100 lines of code
Install `jose` (modern JWT library) and `bcryptjs` (password hashing). Write `lib/auth.ts` with: `hashPassword(plain)` returning a bcrypt hash, `verifyPassword(plain, hash)` returning boolean, `createToken({ userId, email })` returning a signed JWT, `verifyToken(token)` returning the payload or null.
Build `app/api/auth/signup/route.ts` and `app/api/auth/login/route.ts` that validate input (Zod is the standard), look up or create the user in your DB, hash the password, and on success set an httpOnly secure cookie containing the JWT.
Add a middleware that reads the cookie, verifies the JWT, and either allows the request through (authenticated) or redirects to /login. Use the `next/server` `NextResponse.redirect` from middleware for unauthenticated requests.
Security essentials regardless of approach
Cookies: `httpOnly: true`, `secure: true` in production, `sameSite: 'strict'` or `'lax'`. Without these, your auth cookie is reachable by client JavaScript (XSS exposure) or sent on cross-site requests (CSRF exposure).
Passwords: bcrypt or Argon2 only — never MD5, SHA-1, or unsalted SHA-256. The whole point is to be slow enough that brute-force attacks are infeasible.
Rate limiting: limit failed-login attempts per IP and per username. Without it, attackers can credential-stuff your login endpoint freely.
Session length: long-lived refresh tokens + short-lived access tokens is the modern pattern. Keep access tokens to 15–60 minutes; refresh tokens to 7–30 days. Or: a simple sliding-window session (renewed on activity) that expires after 7 days of inactivity.
Production checklist: HTTPS everywhere, rotated secret keys (JWT_SECRET), email verification on signup, password reset flow with time-limited tokens, optional 2FA for sensitive accounts.
How to do it
- 1
Pick your auth approach
Clerk for fastest setup with social login. Auth.js for full control + open source. Custom JWT for simple email-and-password apps. The rest of the guide details each path.
- 2
Install dependencies
Clerk: `npm i @clerk/nextjs`. Auth.js: `npm i next-auth@beta` plus a DB adapter. Custom JWT: `npm i jose bcryptjs zod`.
- 3
Add environment variables
Clerk: NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY + CLERK_SECRET_KEY. Auth.js: AUTH_SECRET, AUTH_URL, provider client IDs/secrets. Custom: JWT_SECRET.
- 4
Build the auth pages and middleware
Add /login, /signup, and middleware.ts that protects /dashboard and /editor (or your auth-gated paths).
- 5
Wire the user table
Clerk/Auth.js: configure the DB adapter for your stack. Custom: a User model with id, email, hashed_password, name, createdAt, updatedAt.
- 6
Test the flows
Sign up, sign in, sign out, protected route access, password reset (if applicable), session expiration. Use a private browsing window to validate the unauthenticated path.
Frequently asked questions
Is Clerk overkill for a small project?
If your project will stay small and free tier is enough, Clerk is one of the easiest paths — 30 minutes from zero to working auth. The 'overkill' concern is mainly about cost at scale (past 10k monthly active users) or wanting full control over user data. For most early-stage projects, Clerk is the right speed/quality tradeoff.
Auth.js or Clerk for a SaaS?
Both work. Clerk wins on speed-to-working. Auth.js wins on no-vendor + free forever + maximum flexibility. For a SaaS where auth is a means to an end (not a feature), Clerk is usually the right call. For a SaaS where you'll heavily customize user flows (multi-tenant, complex roles, embedded identity), Auth.js gives you more room.
Can I switch later?
Yes, but it's painful — you'll need to migrate password hashes (impossible — users have to reset), session state, and any auth-specific schema. Start with the right choice for the next 12 months; switching is feasible but not casual.
Do I need 2FA?
Strongly recommended for SaaS dealing with paid accounts, sensitive data, or business-critical workflows. Clerk has 2FA built-in. Auth.js has community adapters. Custom JWT needs TOTP (via `otpauth` or similar). It's not a launch blocker for a brand-new product, but add it before you have >100 paying customers.
Ready to build?
Try InBuild for free — describe what you want, get a complete site in 30 seconds, export the code anytime.
Start freeMore guides
The Ultimate Guide to AI Website Builders (2026)
How AI website builders work, what they're good at, where they fall short, and how to pick one — Lovable, v0, Bolt, Replit, InBuild compared with honest tradeoffs.
ReadTutorialThe Complete Guide to SaaS Landing Pages (2026)
Anatomy of a SaaS landing page that converts: hero, social proof, features, pricing, FAQ. What every section does, what good looks like, and how to build it in 30 seconds.
Read