Changer le thème

NextAuth.js : tutoriel d'introduction — Credentials, sessions JWT vs base de données

Easton editorial illustration: component assembly loom

Ouvrir la documentation NextAuth.js, c’est se retrouver face à une masse d’options : Provider, Session, Adapter, JWT, Callbacks… Chaque terme est familier, mais ensemble le tableau reste flou. La doc dit « JWT par défaut », puis « avec une base on bascule en Session » — laquelle choisir ?

J’ai failli abandonner pour Clerk. Clerk est rapide, mais au-delà de 10 000 utilisateurs actifs mensuels c’est payant, et beaucoup de personnalisations sont limitées. NextAuth.js demande plus d’effort au départ, mais reste gratuit et entièrement sous votre contrôle.

Cet article ne liste pas toutes les options (ce serait épuisant). Il se concentre sur le scénario Credentials — identifiant/mot de passe, gestion de session, intégration base. Une fois ces briques comprises, NextAuth.js devient beaucoup plus abordable.

Comprendre les concepts clés de NextAuth.js

Que fait vraiment NextAuth.js ?

En une phrase : c’est un middleware d’authentification qui gère qui est connecté et comment l’état de connexion est stocké. Peu importe votre base ou votre UI — NextAuth.js vérifie l’identité et persiste la session.

Comparaison avec Clerk et Supabase Auth :

  • Clerk : belle UI de connexion, tableau de bord utilisateurs, prêt en 30 minutes — payant au-delà de 10 000 MAU
  • Supabase Auth : si vous utilisez Supabase, l’auth est quasi incluse et très bien intégrée
  • NextAuth.js : gratuit et open source — UI de connexion, inscription et logique base à construire vous-même

En 2025, NextAuth.js reste très utilisé, mais les solutions « clé en main » comme Clerk gagnent du terrain. Compréhensible — peu envie de passer des jours sur l’auth plutôt que sur le produit. Pour un projet perso ou un contrôle total, NextAuth.js vaut toujours le coup.

Trois notions indispensables

1. Provider : comment l’utilisateur se connecte ?

C’est la méthode de login. NextAuth.js en supporte plus de 50 ; les plus courantes :

  • OAuth : Google, GitHub, Facebook — un clic, pas de gestion de mot de passe côté vous
  • Credentials : identifiant + mot de passe — classique, et le cœur de cet article

Attention : Credentials est le plus flexible mais demande le plus de code maison. Officiellement ce n’est même pas recommandé — hash des mots de passe, anti brute-force, sessions : tout repose sur vous.

2. Session : comment l’app « se souvient » ?

Après une connexion, on ne retape pas le mot de passe à chaque requête. Deux mécanismes :

  • JWT Session : infos chiffrées dans un cookie, rien côté serveur
  • Database Session : cookie = ID, données en base

Le choix entre les deux est ce qui embrouille le plus les débutants — détail dans la section suivante.

3. Adapter : où sont les données utilisateur ?

Pour stocker email, date d’inscription, etc., il faut un Adapter (Prisma, MongoDB, MySQL…).

Point clé : Credentials ne crée pas automatiquement les comptes en base. Vous gérez les comptes ; NextAuth.js ne fait que valider à la connexion.

JWT vs Session : que choisir ?

C’est la question centrale. Après de nombreux articles, la différence m’est devenue claire.

JWT Session : mode passeport

Photo, nom, validité sur le document ; à la frontière, le passeport suffit. Après login, le serveur émet un token chiffré (userId, email…) stocké dans un cookie.

Avantages :

  • Rapide — pas de requête base
  • Économique — pas de table session, idéal serverless
  • Scalable — pas de table session qui explose

Inconvénients :

  • Pas de déconnexion forcée tant que le token est valide (blacklist = retour à la base)
  • Pas de limite de devices simultanés
  • Contenu du token figé jusqu’à expiration — rôle modifié en base ? l’ancien rôle reste dans le token

Database Session : mode carte d’hôtel

La carte ne porte que le numéro de chambre ; le détail est dans le système hôtelier. Cookie = session ID, utilisateur en base.

Avantages :

  • Révocation immédiate — « déconnecter tous les appareils » = supprimer les lignes session
  • Limite de devices possible
  • Mise à jour live des infos (rôle, etc.)

Inconvénients :

  • Requête base à chaque appel
  • Maintenance de la table session
  • Charge base si fort trafic

Arbre de décision

Besoin de forcer une reconnexion ? (mot de passe changé, compte gelé)
  ├─ Oui → Database Session
  └─ Non → suite

Voulez-vous éviter la base ou déployer en serverless ?
  ├─ Oui → JWT
  └─ Non → suite

Projet perso / MVP, mise en ligne rapide ?
  ├─ Oui → JWT (simple)
  └─ Non → Database Session (entreprise, plus sûr)

Mon premier projet était en JWT ; avec un back-office et le besoin d’« expulser » un utilisateur, migration vers Database Session — coûteuse. Mieux vaut trancher tôt.

Cas particulier : Credentials + Database Session

La doc officielle indique que ce combo n’est pas supporté. OAuth (Google, GitHub) fonctionne ; pour Credentials, NextAuth.js n’attend pas de création automatique de session.

Des contournements existent (callback signIn manuel), voir cette discussion. En débutant : JWT ou OAuth — évitez la complexité inutile.

Configuration complète du Credentials Provider

Assez de théorie — passons au code. Trois niveaux : minimal, options détaillées, exemple complet.

Base : version minimale fonctionnelle

Dépendances :

npm install next-auth

Créer le fichier. App Router (Next.js 13+) : app/api/auth/[...nextauth]/route.ts ; Pages Router : pages/api/auth/[...nextauth].js. Exemple App Router.

app/api/auth/[…nextauth]/route.ts :

import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"

const handler = NextAuth({
  providers: [
    CredentialsProvider({
      name: 'Credentials',
      credentials: {
        email: { label: "E-mail", type: "email" },
        password: { label: "Mot de passe", type: "password" }
      },
      async authorize(credentials) {
        // Utilisateur de test en dur
        if (credentials?.email === "[email protected]" &&
            credentials?.password === "123456") {
          return {
            id: "1",
            name: "Utilisateur test",
            email: "[email protected]"
          }
        }
        return null  // Échec de connexion
      }
    })
  ],
  session: {
    strategy: "jwt"
  },
  pages: {
    signIn: '/login'
  }
})

export { handler as GET, handler as POST }

Variables .env.local :

NEXTAUTH_SECRET=your-super-secret-key-change-this
NEXTAUTH_URL=http://localhost:3000

Points clés :

  • NEXTAUTH_SECRET : chiffre les tokens — obligatoire en prod. Génération : openssl rand -base64 32
  • authorize : cœur de la validation — objet utilisateur = succès, null = échec

Ça tourne, mais l’utilisateur est en dur — ensuite, branchement base.

Options de configuration détaillées

1. session.strategy : jwt ou database ?

Par défaut "jwt". Avec Adapter → "database". Credentials + JWT : définir explicitement strategy: "jwt" pour éviter les erreurs.

2. callbacks : champs personnalisés dans la session

Par défaut, useSession ne renvoie que name, email, image. Pour userId ou role, utilisez les callbacks :

callbacks: {
  async jwt({ token, user }) {
    if (user) {
      token.userId = user.id
    }
    return token
  },
  async session({ session, token }) {
    session.user.userId = token.userId
    return session
  }
}

3. pages : page de connexion personnalisée

La page par défaut /api/auth/signin est fonctionnelle mais peu esthétique. UI maison : pages: { signIn: '/login' }.

Sur la page de login :

import { signIn } from "next-auth/react"

const handleSubmit = async (e) => {
  e.preventDefault()
  const result = await signIn('credentials', {
    redirect: false,
    email,
    password
  })

  if (result?.error) {
    // Échec
  } else {
    // Succès, redirection
  }
}

Exemple complet : inscription + connexion + session

Avec Prisma + PostgreSQL.

1. Table utilisateur

prisma/schema.prisma :

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  password  String
  name      String?
  createdAt DateTime @default(now())
}

Puis npx prisma migrate dev.

2. API d’inscription

app/api/register/route.ts :

import { NextResponse } from "next/server"
import bcrypt from "bcryptjs"
import { prisma } from "@/lib/prisma"

export async function POST(req: Request) {
  try {
    const { email, password, name } = await req.json()

    const existingUser = await prisma.user.findUnique({
      where: { email }
    })

    if (existingUser) {
      return NextResponse.json(
        { error: "E-mail déjà enregistré" },
        { status: 400 }
      )
    }

    const hashedPassword = await bcrypt.hash(password, 10)

    const user = await prisma.user.create({
      data: {
        email,
        password: hashedPassword,
        name
      }
    })

    return NextResponse.json({
      user: {
        id: user.id,
        email: user.email,
        name: user.name
      }
    })
  } catch (error) {
    return NextResponse.json(
      { error: "Échec de l'inscription" },
      { status: 500 }
    )
  }
}

Important : hasher les mots de passe — bcrypt ou argon2, jamais en clair.

3. NextAuth avec base

app/api/auth/[...nextauth]/route.ts :

import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import bcrypt from "bcryptjs"
import { prisma } from "@/lib/prisma"

const handler = NextAuth({
  providers: [
    CredentialsProvider({
      credentials: {
        email: { label: "E-mail", type: "email" },
        password: { label: "Mot de passe", type: "password" }
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) {
          return null
        }

        const user = await prisma.user.findUnique({
          where: { email: credentials.email }
        })

        if (!user) {
          return null
        }

        const isValid = await bcrypt.compare(
          credentials.password,
          user.password
        )

        if (!isValid) {
          return null
        }

        return {
          id: user.id,
          email: user.email,
          name: user.name
        }
      }
    })
  ],
  session: {
    strategy: "jwt",
    maxAge: 30 * 24 * 60 * 60
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.userId = user.id
      }
      return token
    },
    async session({ session, token }) {
      session.user.userId = token.userId as string
      return session
    }
  },
  pages: {
    signIn: '/login'
  }
})

export { handler as GET, handler as POST }

4. Utiliser la session

Server Component :

import { getServerSession } from "next-auth"

export default async function ProfilePage() {
  const session = await getServerSession()

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

  return <div>Bienvenue, {session.user.name}</div>
}

Client Component :

'use client'
import { useSession } from "next-auth/react"

export default function Dashboard() {
  const { data: session, status } = useSession()

  if (status === "loading") {
    return <div>Chargement…</div>
  }

  if (!session) {
    return <div>Veuillez vous connecter</div>
  }

  return <div>Votre ID : {session.user.userId}</div>
}

Rappel : serveur → getServerSession() ; client → useSession() avec 'use client' et <SessionProvider> dans le layout.

Stratégies de session et problèmes fréquents

Bien utiliser la session JWT

1. Infos supplémentaires (userId, role)

Le callback jwt remplit le token ; session expose à l’app.

Exemple avec rôle :

callbacks: {
  async jwt({ token, user }) {
    if (user) {
      token.userId = user.id
      token.role = user.role
    }
    return token
  },
  async session({ session, token }) {
    session.user.userId = token.userId as string
    session.user.role = token.role as string
    return session
  }
}

2. Expiration du token

Par défaut 30 jours :

session: {
  strategy: "jwt",
  maxAge: 7 * 24 * 60 * 60
}

Le token survit à la fermeture du navigateur jusqu’à déconnexion manuelle. « Déconnexion à la fermeture du navigateur » = logique client, pas JWT seul.

3. Limite JWT : pas de révocation instantanée

Compte compromis ? Tant que le token est valide, l’accès continue.

Palliatifs : durée courte, blacklist (retour base), ou Database Session.

Database Session (et Credentials)

Avec OAuth, c’est simple :

npm install @next-auth/prisma-adapter
import { PrismaAdapter } from "@next-auth/prisma-adapter"
import { prisma } from "@/lib/prisma"

const handler = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!
    })
  ]
})

Tables User, Session, Account créées automatiquement ; strategy devient database.

Credentials + database session : non supporté officiellement. Workarounds via signIn — risqué pour débutants. Voir :

Sinon Clerk ou Supabase Auth.

Cinq pièges les plus courants

1. NEXTAUTH_SECRET manquant en production

En local, optionnel (avec avertissement) ; sur Vercel/Railway sans variable → 500. openssl rand -base64 32, puis variable d’environnement.

2. Credentials + database session

Adapter + Credentials → erreur. Soit session: { strategy: "jwt" }, soit logique session manuelle.

3. useSession dans un Server Component

App Router = Server Components par défaut. useSession() échoue. Serveur : getServerSession().

4. Cookie / domaine

localhost:3000 vs example.com — mauvais domaine, session perdue. NEXTAUTH_URL correct en prod, pas de hardcode localhost.

5. JWEDecryptionFailed

NEXTAUTH_SECRET changé, ancien token dans le navigateur. Supprimer les cookies ou se reconnecter.

Bonus : protéger des routes avec Middleware

middleware.ts :

export { default } from "next-auth/middleware"

export const config = {
  matcher: ["/dashboard/:path*", "/profile/:path*"]
}

/dashboard et /profile exigent une connexion.

Recommandations projet et suite

Quelle solution choisir ?

Scénario 1 : projet perso / MVP / blog

  • Recommandation : NextAuth.js + JWT + Credentials
  • Pourquoi : gratuit, simple, pas de table session
  • Inconvénient : migration si besoin de « kick » plus tard

Scénario 2 : entreprise / SaaS (avec budget)

  • Recommandation : Clerk
  • Pourquoi : rapide, belle UI, gestion utilisateurs — économise 40–80 h de dev
  • Inconvénient : coût au-delà de 10 000 MAU
  • Note : avec Supabase, Supabase Auth (50 000 MAU en gratuit)

Scénario 3 : entreprise (sans budget / contrôle total)

  • Recommandation : NextAuth.js + Database Session + OAuth
  • Pourquoi : gratuit, sessions révocables
  • Inconvénient : UI et base à maintenir

Scénario 4 : système utilisateurs existant

  • Recommandation : NextAuth.js + Credentials + JWT
  • Pourquoi : flexible, structure DB inchangée
  • Attention : sécurité (hash, brute-force) à votre charge

Ressources

Conclusion

Trois décisions :

  1. Méthode de login ? Credentials ou OAuth — souvent OAuth (Google/GitHub) est plus simple
  2. Stockage session ? JWT ou Database — perso → JWT, entreprise → Database
  3. Validation ? Credentials = votre logique ; OAuth = le fournisseur

Commencez par l’exemple minimal de cet article — connectez-vous d’abord, enrichissez ensuite.

NextAuth.js a une courbe d’apprentissage ; une fois maîtrisé, vous contrôlez tout le flux. Pour aller vite : Clerk. Pour économiser et apprendre : NextAuth.js.

À lire ensuite :

  • Guide complet Next.js Middleware (protection de routes, A/B tests)

  • RBAC : voir notre article sur la conception des permissions

Configuration complète du login Credentials avec NextAuth.js

De l'installation à la mise en place du login par identifiant/mot de passe et de la gestion des sessions

⏱️ Estimated time: 2 hr

  1. 1

    Step 1: Installer et initialiser NextAuth.js

    Installer les dépendances :
    • npm install next-auth
    • Créer app/api/auth/[...nextauth]/route.ts

    Configuration de base :
    • Définir NEXTAUTH_URL (local : http://localhost:3000)
    • Définir NEXTAUTH_SECRET (chaîne aléatoire)
    • Configurer le tableau providers
  2. 2

    Step 2: Configurer le Credentials Provider

    Implémenter la logique de validation :
    1. Valider l'utilisateur dans authorize de CredentialsProvider
    2. Interroger la base (ou utilisateur de test en dur)
    3. Vérifier le mot de passe (bcrypt, etc.)
    4. Retourner l'objet utilisateur (id, name, email, etc.)

    Attention :
    • authorize doit retourner un objet utilisateur ou null
    • L'objet retourné est stocké dans la session
    • La vérification du mot de passe doit être côté serveur
  3. 3

    Step 3: Choisir la stratégie de session (JWT ou Session)

    Stratégie JWT (par défaut) :
    • Adaptée aux apps sans état
    • Infos de session dans le token JWT
    • Pas de base de données requise
    • Config : session: { strategy: 'jwt' }

    Stratégie Session (base requise) :
    • Adaptée si vous devez révoquer des connexions
    • Infos en base de données
    • Adapter nécessaire (Prisma, MongoDB, etc.)
    • Config : session: { strategy: 'database' }
  4. 4

    Step 4: Configurer les callbacks pour personnaliser le flux

    Callbacks courants :
    • signIn : autoriser ou refuser la connexion
    • jwt : personnaliser le contenu du JWT (stratégie JWT)
    • session : personnaliser le contenu de la session

    Exemple :
    callbacks: {
    async jwt({ token, user }) {
    if (user) token.role = user.role
    return token
    },
    async session({ session, token }) {
    session.user.role = token.role
    return session
    }
    }
  5. 5

    Step 5: Créer la page de connexion et les composants

    API NextAuth.js :
    • signIn('credentials', { username, password }) : déclencher la connexion
    • signOut() : se déconnecter
    • useSession() : récupérer la session courante
    • SessionProvider : envelopper l'app pour le contexte de session

    Exemple :
    const { data: session } = useSession()
    if (session) {
    return <div>Connecté : {session.user.name}</div>
    }
  6. 6

    Step 6: Tester et déboguer

    Points de test :
    • Identifiants corrects → connexion OK
    • Mauvais identifiants → refus
    • Persistance de la session
    • Déconnexion efface la session

    Débogage :
    • Console navigateur
    • Logs serveur
    • Mode debug NextAuth.js
    • Vérifier les variables d'environnement

FAQ

Quelle différence entre NextAuth.js, Clerk et Supabase Auth ?
NextAuth.js :
• Solution open source auto-hébergée, entièrement gratuite
• UI de connexion et gestion utilisateurs à implémenter vous-même
• Mais contrôle total

Clerk :
• UI et interface d'administration complètes
• Payant au-delà de 10 000 utilisateurs actifs mensuels

Supabase Auth :
• Idéal si vous utilisez la base Supabase
• Intégration simple mais couplage à la base
JWT ou Session : que choisir ?
JWT convient aux apps sans état et aux projets personnels sans base, mais on ne peut pas révoquer une session individuelle. Session convient aux apps entreprise avec besoin de révocation ; il faut un Adapter base de données, mais contrôle précis des sessions.
Le login Credentials est-il sécurisé ?
Oui, à condition de :
1) Stocker les mots de passe chiffrés (bcrypt, etc.)
2) Valider côté serveur
3) Utiliser HTTPS
4) Exiger une politique de mot de passe
5) Envisager un captcha anti brute-force
Comment personnaliser la page de connexion ?
Dans la config NextAuth : pages: { signIn: '/auth/login' }.

Créez ensuite votre page et appelez signIn('credentials', { username, password }).

Vous pouvez aussi construire toute l'UI vous-même en n'utilisant que l'API NextAuth.js.
Comment obtenir l'utilisateur connecté ?
Côté client : hook useSession() :
const { data: session } = useSession()

Côté serveur : getServerSession() :
const session = await getServerSession(authOptions)

L'objet session contient user (id, name, email, etc.).
Comment gérer rôles et permissions ?
Dans les callbacks, enrichissez JWT ou Session avec un champ role. Sur les pages ou routes API, vérifiez session.user.role. Le Middleware permet des contrôles globaux.
Quelles bases NextAuth.js prend-il en charge ?
Via Adapter : Prisma (PostgreSQL, MySQL, SQLite, etc.), MongoDB, TypeORM, Drizzle ORM, etc. Avec Adapter, passage automatique en stratégie Session ; les infos de session sont en base.

10 min de lecture · Publié le: 19 déc. 2025 · Mis à jour le: 27 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog