JWT ou Session ? Arrêtez de tergiverser — après cet article, vous saurez quoi choisir

Le curseur clignote entre session: { strategy: "jwt" } et session: { strategy: "database" }. La mise en production approche, et vous hésitez encore entre JWT et Session.
En choisissant une stack, les deux options semblent valables — mais laquelle convient à votre projet ? JWT est stateless et sans requête BDD, mais non révocable instantanément ; Session est stateful et contrôlable, mais chaque requête interroge la BDD. Cet article compare ces deux stratégies de session pour vous aider à décider.
Comprendre d’abord ce que c’est
Beaucoup connaissent les définitions de JWT et Session, mais les expliquer clairement reste difficile. J’aime cette analogie :
La Session, c’est comme une carte de membre de salle de sport. À l’inscription, la salle enregistre vos infos dans son système. À chaque visite, vous passez la carte (un Session ID) et l’accueil retrouve qui vous êtes, la date d’expiration et les cours restants. Tout est stocké en base.
Le JWT, c’est comme une carte d’identité. Vos infos (nom, date de naissance, adresse…) sont imprimées sur le document. Pour prouver votre identité, vous le montrez — pas besoin de consulter un système externe.
Le fonctionnement devient alors clair :
-
Session : après connexion, le serveur génère un Session ID, stocke les infos utilisateur en BDD, envoie le Session ID au navigateur via cookie. À chaque requête, le navigateur renvoie ce Session ID et le serveur interroge la BDD.
-
JWT : après connexion, le serveur encode les infos utilisateur dans un token chiffré (JWT) et l’envoie au navigateur. À chaque requête, le navigateur renvoie le JWT ; le serveur vérifie sa validité sans requête BDD.
JWT en profondeur
Pourquoi le JWT est si populaire ?
Franchement, le JWT cartonne chez les devs, surtout en Serverless et microservices. La raison est simple : pas de gestion de base de données.
Un ami a lancé un site e-commerce sur Vercel avec JWT. Son principal avantage : « zéro prise de tête » — pas de limite de connexions BDD, pas de perf sur une table Session, déploiement edge sans souci. L’utilisateur se connecte une fois, reçoit un JWT, et toutes les requêtes suivantes portent ce token ; le serveur vérifie la signature, c’est tout.
Le JWT convient particulièrement à :
1. Serverless / edge
Sur Vercel, Cloudflare Workers, etc., le JWT est quasi standard. Les fonctions sont stateless ; chaque requête peut être traitée sur un serveur différent. Avec Session, il faudrait un Redis partagé — plus lourd. Avec JWT, le token contient tout ; n’importe quel serveur convient.
2. Haute concurrence
En flash sale, les requêtes BDD deviennent un goulot. Avec Session, chaque requête interroge la BDD ; avec JWT, une seule requête à la connexion, puis validation locale — bien plus rapide.
3. Architecture microservices
Avec plusieurs services (utilisateur, commande, paiement), le JWT partage l’authentification sans que chaque service interroge les Session ni un stockage centralisé.
Les pièges du JWT
Ça sonne bien, mais le JWT a des limites — parfois profondes.
Le plus gros piège : pas de révocation instantanée
Un jour, un compte compromis : il fallait déconnecter l’utilisateur immédiatement. Avec Session, supprimer l’enregistrement BDD suffit. Avec JWT, c’est embarrassant — une fois émis, le token reste valide jusqu’à expiration.
Une liste noire ? Possible, mais on perd l’avantage stateless. Chaque requête consulte la liste — autant qu’une table Session.
En pratique, on raccourcit l’expiration (15 min) et on ajoute un refresh token. Même volé, le token n’est valide que 15 minutes au maximum.
Limite de taille des cookies
Le JWT encode les infos utilisateur. Rôles, permissions, préférences… le token grossit. Les cookies navigateur sont limités à 4 Ko — au-delà, ça ne passe plus.
Leçon apprise : ne mettez que l’essentiel (ID utilisateur, expiration) ; le reste se récupère à la demande.
Configuration JWT NextAuth.js en pratique
Avec Next.js, NextAuth.js (Auth.js) est la solution d’auth la plus répandue. Configurer JWT est simple :
// auth.ts
import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [GitHub],
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, // 30 jours
},
callbacks: {
async jwt({ token, user }) {
// À la première connexion, ajouter les infos user au token
if (user) {
token.id = user.id
token.role = user.role
}
return token
},
async session({ session, token }) {
// Exposer les infos du token au client
if (token) {
session.user.id = token.id
session.user.role = token.role
}
return session
},
},
})
Quelques points d’attention :
-
Définissez
NEXTAUTH_SECRET(Auth.js v5 :AUTH_SECRET). Générez une clé avecnpx auth secret. Elle sert à signer et chiffrer le JWT — ne la divulguez pas. -
Session glissante : pour renouveler automatiquement tant que l’utilisateur est actif, configurez
updateAge:
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60,
updateAge: 24 * 60 * 60, // rafraîchissement toutes les 24 h
}
Ainsi, avec une activité dans les 24 h, la session se prolonge sans déconnexion soudaine.
- Nouveauté 2025 : pour Edge Runtime (middleware), scindez la config :
// auth.config.ts — exécutable sur Edge
export default {
providers: [GitHub],
session: { strategy: "jwt" },
}
// auth.ts — opérations BDD, Node.js uniquement
import { PrismaAdapter } from "@auth/prisma-adapter"
import authConfig from "./auth.config"
export const { handlers, auth } = NextAuth({
...authConfig,
adapter: PrismaAdapter(prisma),
})
Edge Runtime ne supporte pas certaines API Node.js. Après scission, le middleware utilise auth.config.ts, les routes serveur le auth.ts complet.
Database Session en profondeur
Quand Session devient obligatoire ?
Malgré la simplicité du JWT, certains scénarios imposent Session.
J’ai travaillé sur un back-office pour une société financière. Exigence claire : déconnecter immédiatement en cas de connexion anormale. Le JWT ne convient pas.
Database Session convient à :
1. Applications nécessitant un contrôle instantané
Limite multi-appareils (un seul appareil connecté), déconnexion forcée (admin), invalidation de toutes les sessions après changement de mot de passe… Simple avec Session ; avec JWT, beaucoup de logique supplémentaire.
2. Exigences de sécurité élevées
Banque, santé, secteur public : chaque requête doit refléter les permissions à jour. Session interroge la BDD à chaque fois — changement de droits effectif immédiatement.
3. Monolithe traditionnel
Serveur et BDD côte à côte : Session sans surcoût notable. Latence faible, contrôle fin des sessions.
Problèmes de performance des Session
Le principal inconvénient : chaque requête interroge la BDD. À fort trafic, goulot d’étranglement.
Optimisations :
-
Redis pour les Session : bien plus rapide qu’une BDD classique, expiration automatique.
-
Infos minimales : stocker uniquement l’ID utilisateur ; le reste à la demande.
-
Expiration raisonnable : nettoyer les Session expirées, sinon la table grossit sans fin.
Configuration Session Next.js en pratique
NextAuth.js utilise JWT par défaut. Pour Database Session, configurez un adapter :
// auth.ts
import NextAuth from "next-auth"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { PrismaClient } from "@prisma/client"
import GitHub from "next-auth/providers/github"
const prisma = new PrismaClient()
export const { handlers, auth } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [GitHub],
session: {
strategy: "database",
maxAge: 30 * 24 * 60 * 60, // 30 jours
updateAge: 24 * 60 * 60, // mise à jour quotidienne
},
})
Prisma requiert une table Session ; npx prisma db push la crée :
// schema.prisma
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model User {
id String @id @default(cuid())
email String @unique
sessions Session[]
}
Astuces d’optimisation :
-
Index sur
sessionTokenetexpires— requêtes bien plus rapides. -
Nettoyage régulier des Session expirées, par tâche planifiée :
// cleanup-sessions.ts
async function cleanupExpiredSessions() {
await prisma.session.deleteMany({
where: {
expires: {
lt: new Date(),
},
},
})
}
// exécution chaque nuit à 3 h
Cadre de décision pratique
Assez de théorie — comment choisir ? Voici un cadre à appliquer à votre projet :
Par type de projet
| Type de projet | Recommandation | Raison |
|---|---|---|
| Application Serverless | JWT | Stateless, pas de stockage Session partagé |
| Monolithe traditionnel | Session | BDD à proximité, coût de requête faible |
| Microservices | JWT ou hybride | Auth inter-services simplifiée |
| Site de contenu / blog | JWT | Besoins d’auth simples, JWT suffit |
| Back-office SaaS | Session | Contrôle fin des permissions |
Par niveau de sécurité
- Faible sensibilité (blog, forum, contenu) : JWT, expiration 30 jours
- Sensibilité moyenne (e-commerce, social) : JWT + expiration courte + refresh token
- Haute sensibilité (finance, santé, back-office) : Session + expiration courte + session glissante
Cas réels
Cas 1 : site e-commerce — JWT
Le site e-commerce de mon ami a choisi JWT :
- Déploiement Vercel, architecture Serverless — JWT adapté
- Fort volume d’utilisateurs — moins de requêtes BDD, coûts réduits
- Contrôle de session modéré — token expirant lentement après déconnexion acceptable
Compromis : expiration JWT à 7 jours au lieu de 30. Même en cas de vol, invalidation automatique sous une semaine.
Cas 2 : système OA — Session
Un OA interne a opté pour Session :
- Gestion stricte des permissions — changement de rôle effectif immédiatement
- Limite du nombre d’appareils connectés simultanément
- Déploiement intranet — latence BDD négligeable
Session clairement plus adaptée. Optimisation : Redis pour les Session, temps de réponse sous 50 ms.
Cas 3 : schéma hybride — approche Clerk
De plus en plus de plateformes (Clerk, etc.) combinent :
- Token court (JWT) : 15 min, pour les appels API
- Token long (Refresh Token) : en BDD, pour renouveler le token court
- Enregistrement Session : sessions actives en BDD, révocation à distance
Performance JWT + contrôle Session. Implémentation plus complexe — pour exigences élevées en sécurité et UX.
Mon conseil
Si vous hésitez encore :
-
JWT par défaut : suffisant dans la plupart des cas, simple à configurer, bonnes perfs.
-
Passez à Session si :
- limite multi-appareils requise
- déconnexion immédiate nécessaire
- finance/santé ou haute sécurité
- invalidation de toutes les sessions après changement de mot de passe
-
Évitez la sur-ingénierie : projet modeste → solution simple d’abord ; optimisez quand le goulot apparaît. Trop de projets démarrent en hybride complexe pour des fonctions jamais utilisées.
Bonnes pratiques pour l’expiration de session
Une fois le choix fait, gérez bien l’expiration — sinon l’UX en souffre.
Expiration JWT
Le pire : l’utilisateur remplit un formulaire, soumet, et reçoit « session expirée » — tout est perdu.
Solution : Refresh Token + renouvellement transparent
Principe :
-
Deux tokens :
- Access Token : 15 min, pour les appels API
- Refresh Token : 30 jours, pour obtenir un nouvel Access Token
-
Le front renouvelle automatiquement l’Access Token avant expiration (ex. 2 min restantes) via le Refresh Token
-
L’utilisateur ne voit rien — pas de déconnexion soudaine
NextAuth.js intègre ce mécanisme ; configurez :
callbacks: {
async jwt({ token, user, account }) {
if (account && user) {
// Première connexion
return {
...token,
accessToken: account.access_token,
accessTokenExpires: Date.now() + account.expires_in * 1000,
refreshToken: account.refresh_token,
}
}
// Access Token encore valide
if (Date.now() < token.accessTokenExpires) {
return token
}
// Access Token expiré — renouvellement via Refresh Token
return refreshAccessToken(token)
},
}
async function refreshAccessToken(token) {
try {
const response = await fetch("https://api.example.com/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
client_id: process.env.OAUTH_CLIENT_ID,
grant_type: "refresh_token",
refresh_token: token.refreshToken,
}),
})
const refreshedTokens = await response.json()
return {
...token,
accessToken: refreshedTokens.access_token,
accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000,
refreshToken: refreshedTokens.refresh_token ?? token.refreshToken,
}
} catch (error) {
return {
...token,
error: "RefreshAccessTokenError",
}
}
}
Expiration Session
Plus simple, mais avec des nuances.
Session glissante vs timeout absolu
- Session glissante : activité prolonge la session — adaptée à la plupart des cas
- Timeout absolu : déconnexion forcée à l’échéance, activité ou non — banque, haute sécurité
Configuration session glissante NextAuth.js :
session: {
strategy: "database",
maxAge: 2 * 60 * 60, // timeout absolu 2 h
updateAge: 30 * 60, // mise à jour si activité dans les 30 min
}
Avec activité toutes les 30 min, la session se prolonge ; au-delà de 2 h, déconnexion forcée.
Timeout d’inactivité
Pour « déconnexion après 15 min sans action », ajoutez un timer côté front :
let idleTimer
function resetIdleTimer() {
clearTimeout(idleTimer)
idleTimer = setTimeout(() => {
// 15 min sans activité — déconnexion
signOut()
}, 15 * 60 * 1000)
}
// Écouter l'activité utilisateur
document.addEventListener("mousemove", resetIdleTimer)
document.addEventListener("keypress", resetIdleTimer)
Tendances 2025
Quelques tendances de cette année qui peuvent influencer votre choix.
Schémas hybrides mainstream
De plus en plus d’équipes voient JWT et Session comme complémentaires :
- JWT pour l’auth API (rapide)
- Session actives en BDD (contrôlable)
- Les deux avantages combinés
Clerk, Auth0, etc. fonctionnent ainsi. Si vous ne voulez pas implémenter vous-même, ces services conviennent.
Impact d’Edge Runtime
Le middleware Next.js tourne sur Edge Runtime — plus favorable au JWT. Auth dans le middleware (ex. protéger /dashboard) : JWT plus pratique.
Passkey et WebAuthn
Hors sujet principal, mais notable : Passkey (WebAuthn) se généralise — empreinte, Face ID, sans mot de passe. Flux d’auth plus complexe, meilleure UX. NextAuth.js v5 supporte Passkey.
Pour conclure
Vous devriez maintenant avoir une vision claire de JWT et Session. Pas de réponse universelle — comprendre forces et faiblesses, puis décider selon votre projet.
Mon expérience : JWT suffit pour la plupart des projets ; Session si contrôle instantané requis. Ne compliquez pas dès le départ — la simplicité est souvent plus fiable.
Encore indécis ? Démarrez avec la config par défaut NextAuth.js (JWT). En cas de blocage, passer à Session reste simple — quelques lignes de config.
Vous hésitez encore à 3 h du matin ? Dormez, décidez demain. Souvent, après une nuit, le problème paraît moins urgent (sourire).
Des retours ou un cas non couvert ? Commentez — la technique se discute mieux à plusieurs.
FAQ
Quelle est la différence fondamentale entre JWT et Session ?
Quand utiliser JWT, quand utiliser Session ?
Comment gérer l'impossibilité de révoquer un JWT instantanément ?
Comment optimiser les performances des Session ?
Comment configurer JWT et Session dans NextAuth.js ?
Comment gérer l'expiration de session et optimiser l'UX ?
Quelles tendances JWT/Session en 2025 ?
11 min de lecture · Publié le: 19 déc. 2025 · Mis à jour le: 27 juil. 2026
Guide complet Next.js
Si vous arrivez depuis la recherche, le plus rapide est de passer à l’article précédent ou suivant de cette série.
Précédent
NextAuth.js : tutoriel d'introduction — Credentials, sessions JWT vs base de données
NextAuth.js vous semble complexe ? Ce guide se concentre sur Credentials, le choix JWT vs Session, avec exemples de code complets et pièges courants pour les développeurs Next.js.
Partie 48 sur 51
Suivant
Guide complet SWR : maîtriser les stratégies de cache et les mises à jour optimistes en pratique
Apprenez les concepts clés et les stratégies de cache de SWR, et simplifiez jusqu'à 90 % de votre code de récupération de données avec un seul Hook. Inclut des exemples pratiques de mises à jour optimistes, une comparaison avec React Query et les bonnes pratiques d'intégration Next.js.
Partie 50 sur 51



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire