← Back to articles

How to Add Authentication to a Next.js App (2026)

Every SaaS needs auth. Here are three ways to add authentication to Next.js in 2026, from simplest to most flexible.

Option 1: Clerk (Fastest Setup)

Clerk gives you a complete auth system with pre-built UI components. 5 minutes from zero to working auth.

Step 1: Install

npm install @clerk/nextjs

Step 2: Add Environment Variables

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_xxx
CLERK_SECRET_KEY=sk_xxx

Step 3: Add Middleware

// middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

const isProtectedRoute = createRouteMatcher(['/dashboard(.*)'])

export default clerkMiddleware(async (auth, req) => {
  if (isProtectedRoute(req)) {
    await auth.protect()
  }
})

export const config = {
  matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
}

Step 4: Add Provider

// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs'

export default function RootLayout({ children }) {
  return (
    <ClerkProvider>
      <html><body>{children}</body></html>
    </ClerkProvider>
  )
}

Step 5: Add Sign-In UI

// app/sign-in/[[...sign-in]]/page.tsx
import { SignIn } from '@clerk/nextjs'

export default function SignInPage() {
  return <SignIn />
}

That's it. You have sign-up, sign-in, email verification, OAuth (Google, GitHub), and session management. Clerk handles the UI, email sending, and security.

Pricing: Free up to 10,000 MAU. Pro: $25/month + $0.02/MAU after 10K.

Best for: SaaS apps that want auth done fast with zero maintenance.

Option 2: NextAuth.js (Auth.js) — Open Source

NextAuth (now Auth.js v5) is the open-source standard. More setup than Clerk, but free and flexible.

Step 1: Install

npm install next-auth@beta

Step 2: Configure Auth

// auth.ts
import NextAuth from 'next-auth'
import GitHub from 'next-auth/providers/github'
import Google from 'next-auth/providers/google'
import Credentials from 'next-auth/providers/credentials'

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    GitHub({
      clientId: process.env.GITHUB_ID,
      clientSecret: process.env.GITHUB_SECRET,
    }),
    Google({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    }),
  ],
})

Step 3: Add API Route

// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth'
export const { GET, POST } = handlers

Step 4: Add Middleware

// middleware.ts
import { auth } from './auth'

export default auth((req) => {
  if (!req.auth && req.nextUrl.pathname.startsWith('/dashboard')) {
    return Response.redirect(new URL('/api/auth/signin', req.nextUrl))
  }
})

Step 5: Use in Components

// Server component
import { auth } from '@/auth'

export default async function Dashboard() {
  const session = await auth()
  if (!session) redirect('/api/auth/signin')
  
  return <h1>Welcome, {session.user.name}</h1>
}
// Client component
'use client'
import { useSession } from 'next-auth/react'

export function UserMenu() {
  const { data: session } = useSession()
  return session ? <p>{session.user.email}</p> : <SignInButton />
}

Pricing: Free and open source. You host the session storage (database adapter needed for production).

Best for: Developers who want full control and don't want to pay for auth.

Option 3: Supabase Auth — Full Backend

Supabase Auth comes free with Supabase. If you're already using Supabase for your database, auth is built in.

Step 1: Install

npm install @supabase/supabase-js @supabase/ssr

Step 2: Create Supabase Client

// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'

export async function createClient() {
  const cookieStore = await cookies()
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() { return cookieStore.getAll() },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) => 
            cookieStore.set(name, value, options))
        },
      },
    }
  )
}

Step 3: Sign Up / Sign In

const supabase = await createClient()

// Sign up
await supabase.auth.signUp({ email, password })

// Sign in
await supabase.auth.signInWithPassword({ email, password })

// OAuth
await supabase.auth.signInWithOAuth({ provider: 'google' })

// Get user
const { data: { user } } = await supabase.auth.getUser()

Step 4: Middleware

// middleware.ts
import { createServerClient } from '@supabase/ssr'
import { NextResponse } from 'next/server'

export async function middleware(request) {
  const response = NextResponse.next()
  const supabase = createServerClient(url, key, { cookies: { ... } })
  const { data: { user } } = await supabase.auth.getUser()
  
  if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }
  
  return response
}

Pricing: Free up to 50,000 MAU. Included with Supabase plan.

Best for: Apps already using Supabase for database. Row-level security ties auth directly to data access.

Which Should You Choose?

ClerkNextAuthSupabase Auth
Setup time5 min30 min20 min
UI components✅ Pre-built❌ Build your own❌ Build your own
Cost$0-25+/moFreeFree (with Supabase)
OAuth providers20+80+20+
Passwordless
MFAPlugin
User management UI✅ Dashboard✅ Dashboard
Organizations/Teams
Open source

Choose Clerk When

  • Want auth done in 5 minutes
  • Need pre-built UI components
  • Need organization/team features
  • Budget allows $25+/month
  • Don't want to maintain auth infrastructure

Choose NextAuth When

  • Want free, open-source auth
  • Need maximum OAuth provider support
  • Want full control over the auth flow
  • Building custom sign-in/sign-up pages
  • Don't want vendor lock-in

Choose Supabase Auth When

  • Already using Supabase for database
  • Want auth + database in one platform
  • Need row-level security
  • Building a real-time app with Supabase

FAQ

Can I switch auth providers later?

Yes, but it's painful. User sessions, tokens, and database schemas differ between providers. Choose carefully upfront.

Is Clerk worth paying for?

If your time is worth more than $25/month (it is), yes. Clerk saves days of auth implementation and maintenance.

Do I need a database for NextAuth?

For development, no (JWT sessions). For production, yes — use a database adapter (Prisma, Drizzle) for persistent sessions.

What about Lucia Auth?

Lucia was deprecated in 2024. The author recommends building auth yourself or using the libraries above.

Bottom Line

Clerk for fastest setup and best DX. NextAuth for free, open-source, maximum control. Supabase Auth if you're already on Supabase.

For most new SaaS apps in 2026: start with Clerk. The time savings justify the cost. Switch to NextAuth if you need to eliminate the dependency.

Get AI tool guides in your inbox

Weekly deep-dives on the best AI coding tools, automation platforms, and productivity software.