Changer le thème

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

Easton editorial illustration: rendering-mode selector

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.

0
Requêtes BDD JWT
Aucune requête après connexion
À chaque requête
Requêtes BDD Session
Chaque requête interroge la BDD
15 min
Expiration JWT recommandée
Avec refresh token
4 Ko
Limite taille cookie
Le token JWT ne doit pas être trop gros
Source: Synthèse d’expérience terrain

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 :

  1. Définissez NEXTAUTH_SECRET (Auth.js v5 : AUTH_SECRET). Générez une clé avec npx auth secret. Elle sert à signer et chiffrer le JWT — ne la divulguez pas.

  2. 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.

  1. 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 :

  1. Redis pour les Session : bien plus rapide qu’une BDD classique, expiration automatique.

  2. Infos minimales : stocker uniquement l’ID utilisateur ; le reste à la demande.

  3. 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 :

  1. Index sur sessionToken et expires — requêtes bien plus rapides.

  2. 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 projetRecommandationRaison
Application ServerlessJWTStateless, pas de stockage Session partagé
Monolithe traditionnelSessionBDD à proximité, coût de requête faible
MicroservicesJWT ou hybrideAuth inter-services simplifiée
Site de contenu / blogJWTBesoins d’auth simples, JWT suffit
Back-office SaaSSessionContrô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 :

  1. JWT par défaut : suffisant dans la plupart des cas, simple à configurer, bonnes perfs.

  2. 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
  3. É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 :

  1. Deux tokens :

    • Access Token : 15 min, pour les appels API
    • Refresh Token : 30 jours, pour obtenir un nouvel Access Token
  2. Le front renouvelle automatiquement l’Access Token avant expiration (ex. 2 min restantes) via le Refresh Token

  3. 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 ?
JWT est stateless : les infos utilisateur sont encodées dans le token, le serveur vérifie la signature sans requête BDD. Session est stateful : les infos sont en BDD, chaque requête doit les valider. JWT convient au Serverless/haute concurrence ; Session aux scénarios nécessitant un contrôle instantané.
Quand utiliser JWT, quand utiliser Session ?
JWT : Serverless/edge, haute concurrence, microservices, sites de contenu/blogs. Session : contrôle instantané (limite multi-appareils, déconnexion forcée), haute sécurité (finance/santé), monolithes traditionnels. Par défaut, privilégiez JWT ; passez à Session si contrôle instantané requis.
Comment gérer l'impossibilité de révoquer un JWT instantanément ?
Un JWT émis reste valide jusqu'à expiration. Solutions : 1) expiration courte (15 min) + refresh token, 2) liste noire (perd l'avantage stateless), 3) Session ou schéma hybride. Dans la plupart des cas, expiration courte + refresh token suffit.
Comment optimiser les performances des Session ?
Chaque requête interroge la BDD — goulot possible. Optimisations : 1) Redis pour les Session (bien plus rapide), 2) infos minimales (ID utilisateur seulement), 3) index sur sessionToken et expires, 4) nettoyage régulier des Session expirées.
Comment configurer JWT et Session dans NextAuth.js ?
JWT : session: { strategy: "jwt", maxAge: 30 jours }, ajouter les infos utilisateur dans callbacks.jwt. Session : PrismaAdapter, session: { strategy: "database" }, table Session requise. Déploiement Edge Runtime : config à scinder.
Comment gérer l'expiration de session et optimiser l'UX ?
JWT : Access Token (15 min) + Refresh Token (30 jours) pour un renouvellement transparent ; le front rafraîchit avant expiration. Session : session glissante (updateAge) — activité prolonge la session. Évitez la déconnexion soudaine pendant la saisie d'un formulaire.
Quelles tendances JWT/Session en 2025 ?
Schémas hybrides mainstream : JWT pour l'API (rapide) + Session en BDD (contrôlable). Edge Runtime favorise le JWT pour l'auth middleware. Passkey/WebAuthn se généralise ; NextAuth.js v5 les supporte. Choisissez selon le besoin réel, sans sur-ingénierie.

11 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