Authentification Next.js : Next-Auth vs Clerk en 2026
Next-Auth (gratuit, flexible) vs Clerk (premium, clé en main). Quel système d'authentification choisir pour votre app Next.js en 2026 ?
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 :
- User clique "Login with Google"
- Next.js API route
/api/auth/signin/googleredirect vers Google - Google callback
/api/auth/callback/google - Next-Auth crée session + JWT
- Session stockée database + cookie
- User authentifié
Clerk (Architecture)
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Browser │ ────▶ │ Clerk API │ ────▶ │ Clerk Cloud │
│ <ClerkProvider> │ (clerk.com) │ │ Database │
└─────────────┘ └──────────────┘ └──────────────┘
│
▼
┌────────────────┐
│ Clerk Components│
│ <SignIn /> │
│ <UserButton /> │
└────────────────┘
Flow :
- User interagit avec
<SignIn />(Clerk component) - Clerk SDK appelle Clerk API (clerk.com)
- OAuth handled par Clerk servers
- Session JWT retourné (signed by Clerk)
- Next.js vérifie JWT via Clerk SDK
- 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
- Signup sur clerk.com
- Créer nouvelle application
- Choisir OAuth providers (Google, GitHub, etc.)
- 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 :
- Export users via Clerk API
- Créer database schema Next-Auth
- Import users (hash passwords)
- Replace
<ClerkProvider>par Next-Auth - Update tous
currentUser()calls
Timeline : 2-3 jours
Next-Auth → Clerk
Difficulté : 🟢 Facile
Steps :
- Créer app Clerk
- Import users via Clerk Backend API
- Replace Next-Auth par
<ClerkProvider> - 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 :
- Budget : Next-Auth si >80k users
- Time : Clerk si launch <2 semaines
- Control : Next-Auth si data sovereignty
- 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 :
Brandon Sueur
Expert en développement web et création de produits numériques. Passionné par les technologies modernes et l'innovation, je partage mes connaissances et retours d'expérience pour aider les équipes à construire de meilleurs produits.
Articles similaires
Découvrez d'autres articles qui pourraient vous intéresser.
TypeScript Strict Mode ROI - Moins Bugs, Plus Productivité Développeurs 2026
TypeScript Strict ROI: -40% bugs production, +25% productivité, +60% confiance refactoring. Migration 15-45k€, break-even 8 mois.
React Hook Form : Le guide complet pour des formulaires performants en 2026
Maîtrisez React Hook Form pour créer des formulaires React performants et accessibles. Guide complet avec validation Zod, intégration TypeScript et patterns avancés.
Turbopack vs Webpack : Benchmark Complet et Migration Next.js 2026
Turbopack promet 10x plus de performance que Webpack. Benchmark réel sur 12 projets, guide de migration Next.js 15 et analyse technique approfondie.