Tutorial9 min read · Updated May 11, 2026

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. 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. 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. 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. 4

    Build the auth pages and middleware

    Add /login, /signup, and middleware.ts that protects /dashboard and /editor (or your auth-gated paths).

  5. 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. 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 free

More guides