Choisir une stratégie d'authentification pour une nouvelle application web
Choisissez l’authentification d’une web app en 2026 : sessions ou JWT, OAuth 2.0/OIDC, passkeys et providers gérés, avec revocation, CSRF et UX.
Pour une nouvelle application web en 2026, la stratégie découle de la nature de votre application : un monolithe à rendu serveur devrait commencer par des sessions côté serveur dans un cookie signé et HttpOnly ; une SPA, un client mobile ou un backend d’API devrait utiliser des access tokens de courte durée avec rotation des refresh tokens stockés dans des cookies HttpOnly ; toute application nécessitant une connexion sociale ou un SSO d’entreprise devrait superposer OAuth 2.0/OIDC ; et si l’authentification n’est pas votre produit, le choix le plus défendable par défaut est un fournisseur géré. Ajoutez les passkeys comme facteur résistant au phishing, quelle que soit la base choisie. Le reste de cet article justifie chaque branche selon des critères concrets — révocation, mise à l’échelle, exposition CSRF/XSS, complexité, et les modes d’échec UX qui n’apparaissent qu’en production.
Le piège dans lequel tombent la plupart des nouvelles applications est d’adopter les JWTs parce qu’un tutoriel les utilisait, sans raison de mise à l’échelle sans état qui justifie le coût de révocation. Il s’agit d’une décision comparative, pas d’un choix par défaut — voici donc les critères, un tableau comparatif, et une matrice qui associe chaque archétype d’application à une recommandation.
Points clés
- Sessions versus tokens est une question de savoir où l’état d’authentification réside — sur le serveur ou chez le client — tandis que les cookies versus l’en-tête
Authorizationdécident uniquement comment les credentials transitent ; confondre les deux est la raison la plus fréquente pour laquelle de nouvelles applications adoptent des JWTs dont elles n’ont pas besoin. - Stocker des JWTs dans
localStorageest désormais un anti-pattern : toute faille XSS sur votre page peut lire et exfiltrer le token, aussi conservez les credentials dans des cookies HttpOnly inaccessibles à la couche JavaScript. - Le vrai problème avec les JWTs n’est pas leur émission — c’est leur révocation ; une déconnexion instantanée nécessite des durées de vie courtes avec rotation des refresh tokens ou une deny-list côté serveur, ce qui réintroduit discrètement l’état que les JWTs étaient censés éliminer.
- OAuth 2.1 est toujours un Internet-Draft de l’IETF (draft-15, mars 2026), pas encore un RFC ratifié, mais sa direction est établie : PKCE obligatoire, correspondance exacte des redirect URIs, et suppression des grants Implicit et password-credentials.
- Si l’authentification n’est pas votre produit, un fournisseur géré (Auth0, Clerk, Supabase Auth, WorkOS, Stytch) est le choix pragmatique par défaut en 2026 — vous héritez des sessions, de la connexion sociale, du MFA et des passkeys sans avoir à gérer la surface d’exposition aux failles.
Le cadre qui clarifie tout : où l’état réside versus comment il transite
Sessions versus tokens est une question de savoir où votre état d’authentification réside — sur le serveur ou dans le credential du client — tandis que les cookies versus l’en-tête Authorization décident uniquement comment ce credential transite via HTTP. Ce sont deux axes orthogonaux, et les traiter comme une seule décision explique pourquoi tant d’équipes adoptent des JWTs dont elles n’ont pas besoin. Cette distinction est le fil directeur de tout ce qui suit, et le fournisseur d’authentification Authgear le formule de la même façon.
Une session est avec état (stateful) : le serveur conserve l’enregistrement faisant autorité et remet au client un identifiant opaque qui y pointe — le modèle de la carte magnétique d’hôtel, où la réception tient la liste maîtresse. Un JWT est sans état (stateless) : les claims sont auto-contenus et signés, si bien que n’importe quel serveur peut les vérifier sans consultation — le modèle du passeport numérique, où tout agent fait confiance à la signature sans appeler l’émetteur. Vous pouvez transmettre l’un ou l’autre via un cookie ou l’en-tête Authorization. Les cookies et les en-têtes sont des transports ; les sessions et les tokens sont de l’architecture.
| Critère | Sessions côté serveur | JWT / tokens auto-contenus |
|---|---|---|
| Où l’état réside | Serveur (store) | Client (token signé) |
| Révocation | Immédiate — suppression de l’enregistrement | Difficile — TTL court + rotation, ou deny-list |
| Mise à l’échelle | Nécessite un store partagé (ex. Redis) | Sans état ; pas de consultation par requête |
| Taille du credential | ~32 octets d’identifiant opaque | Souvent 800+ octets de claims par requête |
| Surface d’attaque principale | CSRF (cookies envoyés automatiquement) | XSS (vol de token depuis un stockage lisible par JS) |
| Meilleur usage | Monolithe à rendu serveur | API-first, SPA, mobile, microservices |
Le contraste de taille illustre en une ligne l’arbitrage sans état versus avec état : un cookie de session transporte environ 32 octets d’identifiant opaque et nécessite une consultation serveur, tandis qu’un JWT transporte les claims en ligne — souvent 800+ octets à chaque requête — et évite la consultation. Vous échangez de la bande passante et une latence de révocation contre la suppression d’un store.
Sessions : le choix par défaut pour les applications à rendu serveur
Discover how at OpenReplay.com.
Pour un monolithe à rendu serveur, commencez par des sessions côté serveur dans un cookie signé, HttpOnly, Secure et SameSite — c’est l’option correcte la plus simple et elle vous offre une révocation instantanée sans effort supplémentaire. Le navigateur envoie le cookie automatiquement, vous ne stockez aucun état d’authentification en JavaScript, et déconnecter un utilisateur se résume à une seule suppression côté serveur.
Le mécanisme se déroule en quatre étapes : l’utilisateur soumet ses credentials, le serveur crée un enregistrement de session et retourne son identifiant dans un cookie, le navigateur rejoue ce cookie à chaque requête, et la déconnexion détruit les deux côtés. Hachez les mots de passe avec Argon2 avant qu’ils atteignent le store — le guide OWASP sur le stockage des mots de passe le désigne comme l’algorithme de premier choix actuel. Générez les identifiants de session avec une entropie élevée et n’y intégrez jamais de données sensibles ; le guide OWASP sur la gestion des sessions est la référence canonique pour la longueur des identifiants, la rotation et les délais d’expiration.
# FastAPI : émettre une session opaque, la stocker côté serveur, définir un cookie sécurisé
import secrets
from fastapi import FastAPI, Response
# client redis + vérification du hash Argon2 omis pour la concision
app = FastAPI()
SESSION_TTL = 60 * 60 * 24 # 24 heures
@app.post("/login")
async def login(response: Response): # vérifier le hash Argon2 en premier
session_id = secrets.token_urlsafe(32) # ~256 bits d'entropie
await redis.setex(f"sess:{session_id}", SESSION_TTL, user_id)
response.set_cookie(
"session", session_id,
httponly=True, # JS ne peut pas le lire -> atténue l'exfiltration XSS
secure=True, # HTTPS uniquement
samesite="lax", # atténue le CSRF sur les navigations de premier niveau
max_age=SESSION_TTL,
)
return {"ok": True}
Cet extrait illustre tout l’intérêt des sessions : le secret ne réside jamais en JavaScript, et le serveur détient la source de vérité. Deux compromis en découlent. Premièrement, la mise à l’échelle : comme l’enregistrement est côté serveur, un déploiement multi-instances nécessite des sessions persistantes (sticky sessions) ou — mieux — un store partagé comme Redis pour que n’importe quelle instance puisse résoudre n’importe quelle session. Deuxièmement, le CSRF : les cookies sont envoyés automatiquement sur les requêtes cross-site, donc associez SameSite à un token CSRF avec une comparaison en temps constant, conformément au guide OWASP sur le CSRF. Le flag HttpOnly ferme le vecteur de lecture XSS ; SameSite associé à un token ferme le vecteur CSRF.
JWTs et tokens : mise à l’échelle sans état avec un coût de révocation
Optez pour des JWTs bruts lorsque vous avez une raison concrète de mise à l’échelle sans état — une API servant plusieurs types de clients, des microservices qui se transmettent l’identité entre eux, ou un accès cross-domaine où les cookies sont peu pratiques — et non par défaut. Le bénéfice est réel : un token signé est vérifié localement contre une clé publique, ce qui vous dispense de la consultation du store à chaque requête et permet à n’importe quel nœud d’authentifier n’importe quelle requête.
Le vrai problème avec les JWTs n’est pas leur émission — c’est leur révocation. Comme le token est auto-contenu, il reste valide jusqu’à son expiration quoi que pense votre serveur. Une déconnexion instantanée nécessite donc soit des durées de vie courtes avec rotation des refresh tokens, soit une deny-list côté serveur que vous consultez à chaque requête — et cette deny-list réintroduit discrètement l’état exact que les JWTs étaient censés éliminer. Le pattern standard est un access token de courte durée associé à un refresh token de plus longue durée et soumis à rotation ; le guide de bonnes pratiques de sécurité OAuth 2.0 (RFC 9700, janvier 2025) est la référence ratifiée pour la rotation des refresh tokens et les courtes durées de vie des access tokens — une source plus fiable que les TTLs arrondis qui circulent dans les articles de blog.
L’endroit où vous stockez le token détermine votre rayon d’impact en cas d’incident. Stocker des JWTs dans localStorage est désormais un anti-pattern : toute faille XSS sur votre page peut le lire et exfiltrer le token, aussi conservez les tokens dans des cookies HttpOnly inaccessibles à la couche JavaScript. Pour les applications navigateur spécifiquement, le draft de bonnes pratiques de l’IETF OAuth for Browser-Based Apps recommande le flux Authorization Code avec PKCE et avertit que du JavaScript malveillant peut faire bien plus que lire un token.
# FastAPI : vérifier un access token avec PyJWT (et non python-jose)
import jwt # PyJWT
from datetime import datetime, timezone
from fastapi import HTTPException
def verify_access_token(token: str, public_key: str) -> dict:
try:
return jwt.decode(
token, public_key,
algorithms=["RS256"], # n'acceptez jamais "none" ; fixez l'algorithme
options={"require": ["exp", "iat", "sub"]},
)
except jwt.ExpiredSignatureError:
raise HTTPException(401, "token expired") # le client doit rafraîchir
except jwt.InvalidTokenError:
raise HTTPException(401, "invalid token")
# Lors de l'émission des tokens, utilisez UTC avec fuseau horaire :
# exp = datetime.now(timezone.utc) + timedelta(minutes=15)
Deux notes de version permettent de rester à jour. Utilisez PyJWT — le tutoriel officiel de sécurité FastAPI a migré vers cette bibliothèque (import jwt) ainsi que vers pwdlib, et python-jose n’est désormais que minimalement maintenu. Et émettez les dates d’expiration avec datetime.now(timezone.utc) : datetime.utcnow() a été déprécié en Python 3.12 et, selon la documentation des suppressions en attente de Python, reste déprécié sans version de suppression planifiée — il fonctionne encore, mais l’appel avec fuseau horaire est le remplacement recommandé.
OAuth 2.0/2.1 et OIDC : identité déléguée quand vous en avez besoin
Utilisez OAuth 2.0 avec OpenID Connect lorsque vous souhaitez que les utilisateurs se connectent avec un fournisseur d’identité existant — « Se connecter avec Google », GitHub, Microsoft, Okta — ou lorsque vous avez besoin d’un SSO d’entreprise. C’est le modèle de la liste VIP : vous ne vérifiez pas l’utilisateur vous-même ; vous faites confiance à un émetteur qui le connaît déjà. OAuth 2.0 gère l’autorisation déléguée (access tokens pour les APIs), et OpenID Connect superpose un token d’identité pour vous indiquer qui est l’utilisateur.
La complexité ne se justifie que lorsque la délégation ou le SSO est une véritable exigence, car le flux implique de nombreuses pièces mobiles — codes d’autorisation, PKCE, scopes, redirect URIs et rotation des clés JWKS. Pour un MVP avec une connexion propriétaire et sans besoin de connexion sociale, OAuth est disproportionné ; vous porterez la mécanique de redirection sans utiliser la délégation pour laquelle elle existe.
Si vous l’adoptez, ciblez le profil moderne. OAuth 2.1 est toujours un Internet-Draft de l’IETF (draft-15, publié le 2 mars 2026), pas encore un RFC ratifié, mais sa direction est établie. Les principales différences par rapport à OAuth 2.0 sont que le PKCE est obligatoire pour tous les clients utilisant le flux Authorization Code, que les redirect URIs doivent être comparées par correspondance exacte de chaîne, et que les grants Implicit et Resource Owner Password Credentials sont supprimés. L’IETF elle-même note que les Internet-Drafts sont valables au maximum six mois et qu’il est inapproprié de les citer autrement que comme « travaux en cours », aussi traitez OAuth 2.1 comme la cible de consolidation — déjà appliquée en pratique par les principaux fournisseurs d’identité — plutôt que comme un standard finalisé. C’est le fond qui compte : utilisez Authorization Code + PKCE pour tous les clients, y compris les clients confidentiels côté serveur, et abandonnez les redirect URIs avec caractères génériques.
Passkeys et WebAuthn : la direction de voyage résistante au phishing
Ajoutez les passkeys comme facteur résistant au phishing, pas comme une religion. Basée sur WebAuthn/FIDO2, une passkey est une paire de clés publique/privée où la clé privée reste dans le matériel sécurisé de l’appareil et signe un défi serveur après une vérification biométrique locale ou un code PIN. Comme le credential est lié à l’origine et n’est jamais transmis, il résiste au phishing, aux attaques par rejeu et au credential stuffing d’une façon que les mots de passe et les codes SMS ne peuvent pas égaler.
Le terrain réglementaire a évolué de façon décisive. Le NCSC a annoncé en avril 2026 que les passkeys devraient désormais être le premier choix de connexion des consommateurs pour tous les services numériques, et qu’il commencera à recommander les passkeys partout où un service les prend en charge, et la vérification en deux étapes là où ce n’est pas le cas — un changement qui se reflète dans une mise à jour continue des recommandations plutôt que dans un changement soudain unique. La prise en charge est désormais universelle : les passkeys sont supportées par les principaux systèmes d’exploitation, notamment Windows, ChromeOS, macOS, Android, iOS et de nombreuses distributions Linux, ainsi que par des navigateurs tels que Chrome, Firefox, Edge et Safari.
La prise en charge des passkeys est résolue ; leur enrôlement ne l’est pas — et c’est la vraie contrainte de sélection. Selon le Passkey Benchmark 2026 de Corbado (un benchmark éditeur, pas une statistique de population neutre), le taux de réussite du premier enrôlement web est d’environ 49–83 % sur iOS mais seulement de 25–39 % sur Windows. Traitez l’UX d’enrôlement et la récupération de compte comme des problèmes de conception de premier ordre : proposez les passkeys comme une mise à niveau opt-in avec un fallback clair, et concevez la récupération avant le lancement, car un appareil perdu sans chemin de récupération signifie un utilisateur bloqué.
Auth-as-a-Service : le choix pragmatique par défaut quand l’auth n’est pas votre produit
Si l’authentification n’est pas votre produit, le choix le plus défendable par défaut en 2026 est un fournisseur géré — Auth0, Clerk, Supabase Auth, WorkOS, ou Stytch — car vous héritez des sessions, de la connexion sociale, du MFA et des passkeys sans avoir à gérer la surface d’exposition aux failles ni la maintenance. Ces fournisseurs livrent des SDKs clé en main qui gèrent le cycle de vie complet, y compris la mécanique de redirection OAuth et les cérémonies WebAuthn que vous devriez sinon construire et déboguer vous-même.
La décision est purement économique. Développer votre propre solution vous donne le contrôle et un coût nul par MAU ; un fournisseur géré vous donne une implémentation robuste, un délai de mise en production plus court, et confie la rotation d’astreinte du chemin critique de sécurité à quelqu’un d’autre. Pour la plupart des nouvelles applications où la connexion est un prérequis plutôt qu’un différenciateur, la voie gérée est le point de départ rationnel — et vous pouvez en migrer ultérieurement si l’échelle ou les coûts l’exigent.
Une matrice de décision pour les méthodes d’authentification
La stratégie suit l’archétype de l’application. Identifiez votre profil dans la ligne correspondante, commencez par là, et ajoutez les passkeys dans tous les cas comme facteur résistant au phishing.
| Archétype d’application | Commencer avec | Ajouter si nécessaire |
|---|---|---|
| Monolithe à rendu serveur | Sessions côté serveur (cookie HttpOnly) | OIDC pour la connexion sociale ; passkeys en opt-in |
| SPA / mobile / backend d’API | Access tokens de courte durée + rotation des refresh tokens, dans des cookies HttpOnly | OIDC pour l’identité tierce |
| Connexion sociale ou SSO d’entreprise requis | OAuth 2.0 + OIDC (cibler le profil 2.1) | Sessions ou tokens pour vos propres surfaces |
| L’auth n’est pas votre produit | Fournisseur géré (Auth0/Clerk/Supabase/WorkOS/Stytch) | — il intègre déjà le reste |
| Haute sécurité / grand public | N’importe quelle base ci-dessus + passkeys | Flux de récupération conçu en amont |
L’architecture de production courante est hybride : sessions pour l’application web à rendu serveur, JWTs pour l’API consommée par les SPAs et les clients mobiles, OIDC pour la connexion sociale, et passkeys comme mise à niveau opt-in. La tendance de fond dans tout cela est d’envoyer le mot de passe moins souvent, dans moins d’endroits, avec plus de portée et de responsabilité — ce qui explique exactement pourquoi les tokens à portée limitée ont remplacé l’authentification Git par mot de passe et pourquoi les passkeys supplantent désormais les mots de passe.
Ce que la relecture de session révèle sur les modes d’échec UX de l’authentification
La relecture de session (session replay) est une technique qui reconstruit le flux réel côté client, et rejouer les parcours d’authentification met en évidence un catalogue reconnaissable de modes d’échec que les logs backend manquent — car choisir la bonne stratégie sur le papier ne garantit pas que les utilisateurs peuvent réellement se connecter (ce sont des capacités de la technique, pas des taux d’incidents mesurés) :
- Boucles de déconnexion silencieuses dues à l’expiration du token. Lorsqu’un refresh token expire en cours de session, une SPA peut rediriger l’utilisateur vers la page de connexion de façon répétée. La relecture montre la boucle de redirection sur la chronologie, que les logs d’erreur manquent car chaque requête individuelle retourne un 401 techniquement valide.
- Enrôlement de passkey abandonné. La relecture montre où les utilisateurs abandonnent la cérémonie WebAuthn — la fenêtre OS apparaît et l’utilisateur annule — ce qui est exactement l’échec que prédisent les faibles taux d’enrôlement sur Windows.
- Impasses de redirection OAuth. Une redirection post-consentement qui aboutit sur une page vide ou d’erreur (redirect URI non correspondante, paramètre
stateperdu) est invisible dans vos logs backend car l’aller-retour se produit hors de votre serveur ; la relecture capture l’impasse telle que l’utilisateur l’a vécue. - Échecs de cookies spécifiques au navigateur. Les problèmes de cookies
SameSiteet Secure qui ne se reproduisent pas en local apparaissent lorsque la relecture corrèle la session en échec avec un navigateur et une version spécifiques.
L’essentiel est que l’authentification est une surface UX, pas seulement un choix de protocole — et la stratégie que vous choisissez détermine lesquels de ces modes d’échec vous déboguerez.
Choisissez la base qui correspond au profil de votre application, sécurisez le stockage des credentials (cookies HttpOnly, jamais localStorage), et ajoutez les passkeys comme facteur opt-in résistant au phishing. Pour la plupart des nouvelles applications, le premier mouvement honnête est un fournisseur géré ou un monolithe sessions-first ; optez pour des JWTs bruts uniquement lorsqu’une véritable exigence de mise à l’échelle sans état justifie le coût de révocation. Une fois la connexion en production, observez le flux réel — l’écart entre une stratégie solide et une stratégie qui fonctionne est exactement le détail UX qui n’apparaît qu’en production.
FAQ
Dois-je utiliser des JWTs par défaut pour une nouvelle application web ?
Non. Utilisez des JWTs bruts uniquement lorsque vous avez une raison concrète de mise à l'échelle sans état, comme une API servant plusieurs types de clients ou des microservices qui se transmettent l'identité entre eux. Pour un monolithe à rendu serveur, les sessions côté serveur sont plus simples et offrent une révocation instantanée. Les JWTs sont auto-contenus, donc les révoquer avant expiration nécessite des durées de vie courtes avec rotation des refresh tokens ou une deny-list côté serveur, ce qui réintroduit l'état serveur exact que les JWTs étaient censés éliminer.
Un token de session est-il la même chose qu'un JWT ?
Non. Un token de session est un identifiant opaque d'environ 32 octets qui pointe vers un état d'authentification stocké sur le serveur, de sorte que chaque requête nécessite une consultation. Un JWT transporte ses claims en ligne et est vérifié localement contre une signature sans consultation, ajoutant souvent 800 octets ou plus par requête. Les deux représentent l'arbitrage sans état versus avec état : une session conserve l'état sur le serveur, tandis qu'un JWT le déplace dans le credential signé du client.
Puis-je stocker un JWT dans localStorage si je filtre mes entrées ?
Non, stocker un JWT dans localStorage est un anti-pattern quelle que soit la validation des entrées, car toute faille de cross-site scripting n'importe où sur la page peut lire localStorage et exfiltrer le token. Conservez les credentials dans des cookies HttpOnly, inaccessibles à JavaScript, et associez-les à SameSite et à un token CSRF. Pour les applications navigateur, le draft de bonnes pratiques IETF OAuth for Browser-Based Apps recommande le flux Authorization Code avec PKCE plutôt que le stockage de tokens lisibles côté client.
OAuth est-il disproportionné pour un MVP avec une connexion propriétaire ?
Oui, si votre MVP n'a besoin que d'une connexion propriétaire sans connexion sociale ni SSO d'entreprise, OAuth 2.0 ajoute une mécanique que vous n'utiliserez pas. Le flux implique des codes d'autorisation, PKCE, des scopes, des redirect URIs et une rotation des clés JWKS, qui existent tous pour prendre en charge l'identité déléguée. Adoptez OAuth avec OpenID Connect uniquement lorsque « Se connecter avec Google », GitHub ou le SSO d'entreprise est une véritable exigence ; sinon, commencez avec des sessions ou un fournisseur géré.