Authentification Next.js : Next-Auth vs Clerk en 2026

Brandon Sueur16 min

L'authentification est le cœur de 89% des applications web modernes.

En 2026, deux solutions dominent l'écosystème Next.js : NextAuth.js (open-source, gratuit, flexible) et Clerk (SaaS premium, clé en main). Ce guide compare les deux sur 12 critères techniques et business.

Spoiler : Next-Auth pour control & budget. Clerk pour vitesse & UX premium.

TL;DR : Next-Auth ou Clerk ?

Critère Next-Auth v5 Clerk Gagnant
Prix Gratuit (hosting seul) 25€-299€/mois 🏆 Next-Auth
Setup Time 2-4 heures 15-30 minutes 🏆 Clerk
Customisation ⭐⭐⭐⭐⭐ Totale ⭐⭐⭐ Limitée 🏆 Next-Auth
UI Components À créer ✅ Inclus (React) 🏆 Clerk
OAuth Providers 80+ (config manuelle) 20+ (1-click) 🏆 Clerk (UX)
Passkeys/WebAuthn ✅ Full control ✅ Built-in ⚖️ Draw
MFA Code custom ✅ Built-in (SMS, TOTP) 🏆 Clerk
User Management Database custom ✅ Dashboard admin 🏆 Clerk
Performance Excellent (self-hosted) Excellent (CDN global) ⚖️ Draw
Sécurité ⭐⭐⭐⭐⭐ (si bien config) ⭐⭐⭐⭐⭐ (managed) ⚖️ Draw
Vendor Lock-in ✅ Aucun ⚠️ Fort 🏆 Next-Auth
Docs Quality ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 🏆 Clerk

Score final : Next-Auth 7/12, Clerk 7/12, Draw 4/12

Architecture Système : Comment Fonctionnent-ils ?

Next-Auth v5 (Architecture)

┌─────────────┐         ┌──────────────┐         ┌──────────────┐
│   Browser   │ ────▶   │  Next.js API │ ────▶   │  Database    │
│             │         │  /api/auth/* │         │  (Postgres)  │
└─────────────┘         └──────────────┘         └──────────────┘
                                │
                                ▼
                        ┌──────────────┐
                        │ OAuth Provider│
                        │ (Google, etc)│
                        └──────────────┘

Flow :

  1. User clique "Login with Google"
  2. Next.js API route /api/auth/signin/google redirect vers Google
  3. Google callback /api/auth/callback/google
  4. Next-Auth crée session + JWT
  5. Session stockée database + cookie
  6. User authentifié

Clerk (Architecture)

┌─────────────┐         ┌──────────────┐         ┌──────────────┐
│   Browser   │ ────▶   │  Clerk API   │ ────▶   │ Clerk Cloud  │
│ <ClerkProvider>       │  (clerk.com) │         │  Database    │
└─────────────┘         └──────────────┘         └──────────────┘
       │
       ▼
┌────────────────┐
│ Clerk Components│
│ <SignIn />     │
│ <UserButton /> │
└────────────────┘

Flow :

  1. User interagit avec <SignIn /> (Clerk component)
  2. Clerk SDK appelle Clerk API (clerk.com)
  3. OAuth handled par Clerk servers
  4. Session JWT retourné (signed by Clerk)
  5. Next.js vérifie JWT via Clerk SDK
  6. User authentifié

Différence clé : Next-Auth = self-hosted, Clerk = SaaS managed

Implémentation Next-Auth v5

Setup Complet (30 minutes)

1. Installation

pnpm add next-auth@beta @auth/drizzle-adapter drizzle-orm
pnpm add -D drizzle-kit

Note : Next-Auth v5 = "Auth.js" (rebrand)

2. Database Schema (Drizzle ORM)

// db/schema.ts
import { pgTable, text, timestamp, primaryKey, integer } from 'drizzle-orm/pg-core'
import { relations } from 'drizzle-orm'

export const users = pgTable('users', {
  id: text('id')
    .primaryKey()
    .$defaultFn(() => crypto.randomUUID()),
  name: text('name'),
  email: text('email').unique().notNull(),
  emailVerified: timestamp('email_verified', { mode: 'date' }),
  image: text('image'),
  createdAt: timestamp('created_at').defaultNow(),
})

export const accounts = pgTable(
  'accounts',
  {
    userId: text('user_id')
      .notNull()
      .references(() => users.id, { onDelete: 'cascade' }),
    type: text('type').notNull(),
    provider: text('provider').notNull(),
    providerAccountId: text('provider_account_id').notNull(),
    refresh_token: text('refresh_token'),
    access_token: text('access_token'),
    expires_at: integer('expires_at'),
    token_type: text('token_type'),
    scope: text('scope'),
    id_token: text('id_token'),
    session_state: text('session_state'),
  },
  (account) => ({
    compoundKey: primaryKey({ columns: [account.provider, account.providerAccountId] }),
  })
)

export const sessions = pgTable('sessions', {
  sessionToken: text('session_token').primaryKey(),
  userId: text('user_id')
    .notNull()
    .references(() => users.id, { onDelete: 'cascade' }),
  expires: timestamp('expires', { mode: 'date' }).notNull(),
})

export const verificationTokens = pgTable(
  'verification_tokens',
  {
    identifier: text('identifier').notNull(),
    token: text('token').notNull(),
    expires: timestamp('expires', { mode: 'date' }).notNull(),
  },
  (vt) => ({
    compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }),
  })
)

// Relations
export const usersRelations = relations(users, ({ many }) => ({
  accounts: many(accounts),
  sessions: many(sessions),
}))

3. Auth Configuration

// auth.ts (root)
import NextAuth from 'next-auth'
import Google from 'next-auth/providers/google'
import GitHub from 'next-auth/providers/github'
import { DrizzleAdapter } from '@auth/drizzle-adapter'
import { db } from '@/db'
import { users, accounts, sessions, verificationTokens } from '@/db/schema'

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: DrizzleAdapter(db, {
    usersTable: users,
    accountsTable: accounts,
    sessionsTable: sessions,
    verificationTokensTable: verificationTokens,
  }),
  providers: [
    Google({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      allowDangerousEmailAccountLinking: true, // Auto-link same email
    }),
    GitHub({
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    }),
  ],
  session: {
    strategy: 'database', // ou 'jwt' pour stateless
    maxAge: 30 * 24 * 60 * 60, // 30 days
  },
  pages: {
    signIn: '/login',
    signOut: '/logout',
    error: '/auth/error',
    verifyRequest: '/auth/verify-request',
  },
  callbacks: {
    async session({ session, user }) {
      // Add user ID to session
      if (session.user) {
        session.user.id = user.id
      }
      return session
    },
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id
      }
      return token
    },
  },
  debug: process.env.NODE_ENV === 'development',
})

4. API Route Handler

// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth'

export const { GET, POST } = handlers

5. Middleware (Route Protection)

// middleware.ts
import { auth } from '@/auth'
import { NextResponse } from 'next/server'

export default auth((req) => {
  const { pathname } = req.nextUrl
  const isAuthenticated = !!req.auth

  // Protected routes
  const protectedRoutes = ['/dashboard', '/profile', '/settings']
  const isProtectedRoute = protectedRoutes.some((route) => pathname.startsWith(route))

  if (isProtectedRoute && !isAuthenticated) {
    return NextResponse.redirect(new URL('/login', req.url))
  }

  // Redirect authenticated users away from login
  if (pathname === '/login' && isAuthenticated) {
    return NextResponse.redirect(new URL('/dashboard', req.url))
  }

  return NextResponse.next()
})

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
}

6. Login Page

// app/login/page.tsx
import { signIn } from '@/auth'
import { Button } from '@/components/ui/button'

export default function LoginPage() {
  return (
    <div className="flex min-h-screen items-center justify-center">
      <div className="w-full max-w-md space-y-4 rounded-lg border p-8">
        <h1 className="text-2xl font-bold">Sign In</h1>

        <form
          action={async () => {
            'use server'
            await signIn('google', { redirectTo: '/dashboard' })
          }}
        >
          <Button type="submit" className="w-full">
            Continue with Google
          </Button>
        </form>

        <form
          action={async () => {
            'use server'
            await signIn('github', { redirectTo: '/dashboard' })
          }}
        >
          <Button type="submit" variant="outline" className="w-full">
            Continue with GitHub
          </Button>
        </form>
      </div>
    </div>
  )
}

7. Protected Page (Server Component)

// app/dashboard/page.tsx
import { auth } from '@/auth'
import { redirect } from 'next/navigation'
import { LogoutButton } from '@/components/logout-button'

export default async function DashboardPage() {
  const session = await auth()

  if (!session) {
    redirect('/login')
  }

  return (
    <div className="p-8">
      <h1>Dashboard</h1>
      <p>Welcome, {session.user?.name}!</p>
      <p>Email: {session.user?.email}</p>
      <img src={session.user?.image || ''} alt="Avatar" className="h-12 w-12 rounded-full" />

      <LogoutButton />
    </div>
  )
}

8. Logout Component (Client)

// components/logout-button.tsx
'use client'

import { signOut } from 'next-auth/react'
import { Button } from '@/components/ui/button'

export function LogoutButton() {
  return <Button onClick={() => signOut({ redirectTo: '/' })}>Sign Out</Button>
}

Setup complet : ~2 heures (avec database schema)

Implémentation Clerk

Setup Complet (15 minutes)

1. Création Compte Clerk

  1. Signup sur clerk.com
  2. Créer nouvelle application
  3. Choisir OAuth providers (Google, GitHub, etc.)
  4. Copier API keys

2. Installation

pnpm add @clerk/nextjs

3. Environment Variables

# .env.local
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx
CLERK_SECRET_KEY=sk_test_xxx

# Redirect URLs
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/dashboard

4. Root Layout Wrapper

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

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <ClerkProvider localization={frFR}>
      <html lang="fr">
        <body>{children}</body>
      </html>
    </ClerkProvider>
  )
}

5. Middleware (Route Protection)

// middleware.ts
import { authMiddleware } from '@clerk/nextjs'

export default authMiddleware({
  publicRoutes: ['/', '/blog(.*)'],
  ignoredRoutes: ['/api/webhook'],
})

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

6. Sign In Page

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

export default function SignInPage() {
  return (
    <div className="flex min-h-screen items-center justify-center">
      <SignIn
        appearance={{
          elements: {
            rootBox: 'mx-auto',
            card: 'shadow-lg',
          },
        }}
      />
    </div>
  )
}

7. Protected Page

// app/dashboard/page.tsx
import { currentUser } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'

export default async function DashboardPage() {
  const user = await currentUser()

  if (!user) {
    redirect('/sign-in')
  }

  return (
    <div className="p-8">
      <h1>Dashboard</h1>
      <p>
        Welcome, {user.firstName} {user.lastName}!
      </p>
      <p>Email: {user.emailAddresses[0].emailAddress}</p>
      <img src={user.imageUrl} alt="Avatar" className="h-12 w-12 rounded-full" />
    </div>
  )
}

8. User Button (Client Component)

// components/header.tsx
import { UserButton } from '@clerk/nextjs'

export function Header() {
  return (
    <header className="border-b">
      <div className="container flex h-16 items-center justify-between">
        <h1>My App</h1>
        <UserButton
          afterSignOutUrl="/"
          appearance={{
            elements: {
              avatarBox: 'h-10 w-10',
            },
          }}
        />
      </div>
    </header>
  )
}

Setup complet : ~15 minutes (sans database schema car managed by Clerk)

Comparatif Détaillé

1. Pricing (Coûts Réels)

Next-Auth

Coûts :

  • Library : Gratuit (open-source)
  • Hosting database : 0-50€/mois (Neon, Supabase)
  • Total : 0-50€/mois

Scaling :

  • 10k users : ~10€/mois
  • 100k users : ~30€/mois
  • 1M users : ~150€/mois (database scaling)

Clerk

Plans 2026 :

  • Free : 10k MAUs (Monthly Active Users) gratuit
  • Pro : 25€/mois (50k MAUs inclus)
  • Enterprise : 299€/mois+ (custom)

Overage pricing :

  • +0.02€ par MAU supplémentaire

Scaling :

  • 10k users : Gratuit
  • 50k users : 25€/mois
  • 100k users : 25€ + (50k × 0.02€) = 1,025€/mois
  • 1M users : ~20,000€/mois

ROI Comparison (100k users) :

  • Next-Auth : ~30€/mois
  • Clerk : ~1,025€/mois

Économie Next-Auth : 995€/mois (33× moins cher)

2. Features Comparison

Feature Next-Auth v5 Clerk
OAuth (Google, GitHub, etc.) ✅ 80+ providers ✅ 20+ providers
Email/Password ✅ (custom) ✅ Built-in
Magic Links ✅ (custom) ✅ Built-in
Passkeys/WebAuthn ✅ Experimental ✅ Production
MFA (2FA) ✅ Custom code ✅ SMS, TOTP, Backup codes
Phone Authentication ❌ Custom ✅ Built-in (SMS)
User Management UI ❌ À créer ✅ Dashboard admin
Profile Management ❌ À créer <UserProfile /> component
Organization/Teams ❌ Custom ✅ Built-in
Role-based Access (RBAC) ✅ Custom ✅ Built-in
Audit Logs ❌ Custom ✅ Enterprise plan
Webhooks ❌ Custom ✅ Built-in
Session Management ✅ Database ou JWT ✅ JWT (managed)
Analytics ❌ Custom ✅ Dashboard

Winner Features : Clerk (built-in vs custom development)

3. Developer Experience

Setup Time

  • Next-Auth : 2-4 heures (database schema, UI custom)
  • Clerk : 15-30 minutes (tout built-in)

Winner : Clerk (8× plus rapide)

Customisation UI

Next-Auth :

// ✅ Control total design
<Button onClick={() => signIn('google')}>
  <GoogleIcon />
  Continue with Google
</Button>

Clerk :

// ⚠️ Customisation limitée (Appearance API)
<SignIn
  appearance={{
    elements: {
      card: 'custom-class', // Tailwind classes
      rootBox: 'mx-auto',
    },
    variables: {
      colorPrimary: '#000000',
    },
  }}
/>

Winner : Next-Auth (design 100% custom)

Type Safety

Next-Auth :

// ✅ Full TypeScript control
declare module 'next-auth' {
  interface Session {
    user: {
      id: string
      role: 'admin' | 'user'
      subscription: 'free' | 'pro'
    } & DefaultSession['user']
  }
}

Clerk :

// ✅ Types built-in
const user = await currentUser()
// user.id, user.firstName, etc. auto-typed

Winner : Draw (both excellent)

4. Performance

Next-Auth (Self-Hosted)

Latency :

  • Session check : 15-30ms (database query)
  • JWT verify : 1-2ms (if JWT strategy)

Optimizations :

// Edge-compatible JWT strategy
export const authConfig = {
  session: { strategy: 'jwt' }, // ✅ No database query
}

Clerk (SaaS)

Latency :

  • Session check : 20-40ms (Clerk API call)
  • JWT verify : 5-10ms (local w/ public key)

CDN Global :

  • Clerk servers replicated globally
  • Edge locations : 200+

Winner : Next-Auth (JWT) (1-2ms vs 5-10ms)

5. Sécurité

Next-Auth

Responsabilités :

  • ✅ CSRF protection (built-in)
  • ✅ Session encryption
  • ⚠️ OAuth config secure (manual)
  • ⚠️ Database security (votre responsabilité)

Security Checklist :

// ✅ Secure cookies
export const authConfig = {
  cookies: {
    sessionToken: {
      name: '__Secure-next-auth.session-token',
      options: {
        httpOnly: true,
        sameSite: 'lax',
        path: '/',
        secure: true, // HTTPS only
      },
    },
  },
}

Clerk

Managed Security :

  • ✅ SOC 2 Type II compliant
  • ✅ GDPR compliant
  • ✅ CCPA compliant
  • ✅ Automatic security updates
  • ✅ DDoS protection
  • ✅ Rate limiting

Winner : Clerk (security managed, certifications)

Use Cases : Quand Choisir Quoi ?

Choisir Next-Auth Si :

Budget serré (<25€/mois auth acceptable) ✅ Scaling >100k users (économies massives) ✅ Control total data (GDPR strict, data sovereignty) ✅ Customisation UI totale (design system custom) ✅ Pas vendor lock-in (migration future facile) ✅ Déjà database (Postgres, MySQL)

Exemples projets :

  • SaaS B2B (data sovereignty EU)
  • Startups budget limité
  • Apps >100k users (scaling)
  • Open-source projects

Choisir Clerk Si :

Time-to-market critique (<1 semaine setup) ✅ Pas équipe backend (product team seulement) ✅ Features avancées needed (MFA, Organizations, Phone auth) ✅ Budget confortable (<100k users, 25-299€/mois OK) ✅ Compliance managée (SOC 2, GDPR délégué)

Exemples projets :

  • MVP startups (launch rapide)
  • Agency client projects (livraison rapide)
  • B2C apps (<50k users)
  • No-code/low-code platforms

Migration Next-Auth → Clerk (ou inverse)

Clerk → Next-Auth

Difficulté : 🟡 Moyenne

Steps :

  1. Export users via Clerk API
  2. Créer database schema Next-Auth
  3. Import users (hash passwords)
  4. Replace <ClerkProvider> par Next-Auth
  5. Update tous currentUser() calls

Timeline : 2-3 jours

Next-Auth → Clerk

Difficulté : 🟢 Facile

Steps :

  1. Créer app Clerk
  2. Import users via Clerk Backend API
  3. Replace Next-Auth par <ClerkProvider>
  4. Update authentication checks

Timeline : 1 jour

Vendor lock-in warning : Clerk migration inverse = plus difficile (data export limité)

Benchmark Performance Réel

Test Setup

App : Next.js 15, 10k users simulés Server : Vercel Pro Database : Neon (Postgres)

Résultats

Métrique Next-Auth (JWT) Next-Auth (DB) Clerk
Session check (p50) 1.2ms 28ms 8ms
Session check (p99) 3ms 85ms 22ms
Login latency 420ms 450ms 380ms
Bundle size (client) 12KB 12KB 45KB
Cold start Impact +15ms +40ms +25ms

Analysis :

  • Next-Auth JWT : Fastest session checks (no DB)
  • Clerk : Best login UX (optimized flow)
  • Next-Auth DB : Slowest (database query overhead)

Winner : Next-Auth (JWT strategy) pour performance pure

Coûts Développement (ROI)

Next-Auth

Setup initial :

  • Database schema : 1h
  • Auth config : 2h
  • Pages UI (login, signup) : 4h
  • Testing : 2h
  • Total : 9 heures (6,300€ @ 700€ TJM)

Maintenance annuelle :

  • Security updates : 2h/an
  • Bug fixes : 4h/an
  • Total : 6h/an (4,200€/an)

Coût 3 ans :

  • Setup : 6,300€
  • Maintenance : 12,600€
  • Hosting : 1,800€ (50€/mois × 36)
  • Total : 20,700€

Clerk

Setup initial :

  • Installation : 15min
  • Config : 15min
  • Testing : 30min
  • Total : 1 heure (700€ @ 700€ TJM)

Subscription 3 ans :

  • Pro plan : 25€/mois × 36 = 900€
  • Overage (avg 30k MAUs) : 0€
  • Total : 900€

Coût 3 ans :

  • Setup : 700€
  • Subscription : 900€
  • Total : 1,600€

Économie Clerk : 19,100€ (3 ans)

Mais : Si scaling >100k users, Next-Auth redevient rentable

Break-Even Point

Calcul :

Clerk devient plus cher quand :

Next-Auth Total Cost < Clerk Total Cost

Setup (6,300€) + Hosting (50€/mois) < Clerk Subscription + Overages

6,300 + (50 × months) < (25 × months) + (MAUs - 50k) × 0.02

Break-even : ~80k MAUs (Monthly Active Users)

Conclusion ROI :

  • <80k MAUs : Clerk plus rentable (dev time économisé)
  • >80k MAUs : Next-Auth plus rentable (scale costs)

Recommendation 2026

Par Profil Projet

Startups Pré-Seed/Seed :

  • 🏆 Clerk (time-to-market > cost)
  • Économie 8h dev (5,600€)
  • Permet focus features core business

Startups Serie A+ :

  • 🏆 Next-Auth (scaling costs)
  • À 100k users : économie 1,000€/mois
  • ROI annuel : 12,000€

Entreprises (B2B SaaS) :

  • 🏆 Next-Auth (data sovereignty)
  • GDPR compliance control
  • Pas vendor lock-in

Agencies (Client Projects) :

  • 🏆 Clerk (livraison rapide)
  • Setup 15min vs 9h
  • Client-friendly admin dashboard

Open-Source Projects :

  • 🏆 Next-Auth (gratuit, self-hosted)
  • Community-driven
  • Pas dépendance SaaS

Notre Choix Hulli Studio

Projets clients : Clerk (95% cas)

  • Livraison rapide
  • Client admin dashboard
  • Support managed

Projets internes : Next-Auth

  • Control total
  • Customisation poussée
  • Pas récurrent costs

Conclusion

Next-Auth et Clerk sont excellents. Le choix dépend de :

  1. Budget : Next-Auth si >80k users
  2. Time : Clerk si launch <2 semaines
  3. Control : Next-Auth si data sovereignty
  4. Features : Clerk si MFA/Organizations needed

Notre top pick 2026 : Clerk pour 80% projets

  • Setup 8× plus rapide
  • UX premium
  • ROI positif <80k users

Exceptions (Next-Auth) :

  • Scaling >100k users
  • GDPR strict (healthcare, finance)
  • Budget dev confortable

FAQ

Clerk compatible Next.js 15 App Router ?

Oui, full support. Clerk SDK optimisé pour Server Components avec currentUser() async.

Next-Auth v5 stable ?

Oui depuis janvier 2025. Production-ready, utilisé par Vercel, Cal.com, etc.

Peut-on migrer Clerk → Next-Auth après ?

⚠️ Oui mais difficile. Export users Clerk API, re-hash passwords, update code. Timeline 2-3 jours. Vendor lock-in modéré.

Next-Auth support Passkeys ?

Oui (experimental). Features WebAuthn depuis v5. Pas encore production-grade comme Clerk.


Articles connexes :