Tokens de connexion sécurisés : JWT, OAuth2
L'authentification par token a supplanté les sessions côté serveur dans la grande majorité des architectures modernes. Les JWT (JSON Web Tokens) sont omniprésents, OAuth2 est le protocole de délégation d'accès dominant, et les refresh tokens permettent des sessions longues sans stocker d'état côté serveur. Pourtant, cette apparente simplicité cache une complexité opérationnelle significative. Une implémentation incorrecte de ces mécanismes ouvre des failles d'authentification critiques, souvent plus faciles à exploiter qu'une injection SQL.
JWT : anatomie et pièges courants
Un JWT est composé de trois parties encodées en Base64url et séparées par des points : l'en-tête (header), la charge utile (payload) et la signature. L'en-tête spécifie l'algorithme de signature utilisé. Le payload contient les claims — des assertions sur l'entité identifiée. La signature garantit l'intégrité de l'ensemble.
Les claims standardisés les plus importants sont exp (expiration), iat (date d'émission), nbf (not before), iss (émetteur), aud (audience) et sub (sujet). Une erreur fréquente est de valider uniquement la signature sans vérifier ces claims. Un token expiré avec une signature valide devrait être systématiquement rejeté.
Plusieurs pièges classiques méritent une attention particulière. L'algorithme none dans l'en-tête permet à un attaquant de soumettre un token sans signature si la bibliothèque de vérification l'accepte — une vulnérabilité documentée dans de nombreuses implémentations historiques. La confusion entre algorithmes asymétriques et symétriques est un autre vecteur : un serveur configuré pour vérifier avec RS256 peut être trompé par un token signé avec HS256 en utilisant la clé publique comme secret HMAC, si la bibliothèque n'impose pas explicitement l'algorithme attendu. La règle est simple : toujours spécifier l'algorithme attendu lors de la vérification, ne jamais le lire depuis le token lui-même.
Le payload d'un JWT est encodé, non chiffré. Toute information qu'il contient est lisible par quiconque possède le token. Ne jamais y stocker de données sensibles — mots de passe, numéros de carte, données médicales — ni d'informations utilisées pour prendre des décisions de sécurité qui ne seraient pas re-vérifiées côté serveur. Pour comprendre la couche en amont, voir le hachage sécurisé des mots de passe avant émission du token.
Access token vs refresh token : deux rôles distincts
La distinction entre access token et refresh token est fondamentale et répond à des impératifs de sécurité opposés : un access token doit être court, un refresh token peut être long mais doit être strictement contrôlé.
L'access token est présenté à chaque requête API pour prouver l'identité et les droits de l'appelant. Sa durée de vie courte — typiquement 5 à 15 minutes — limite la fenêtre d'exploitation en cas de vol. Si un access token est intercepté, l'attaquant ne dispose que d'une courte période avant son expiration. La révocation immédiate d'un access token dans un modèle stateless est complexe à implémenter ; la courte durée de vie est la principale mitigation.
Le refresh token est utilisé uniquement pour obtenir un nouvel access token auprès du serveur d'autorisation, sans que l'utilisateur ait à se réauthentifier. Sa durée de vie est longue — de quelques heures à plusieurs semaines selon le contexte. Il ne doit jamais être transmis aux APIs de ressources, uniquement au serveur d'autorisation sur un endpoint dédié. Cette séparation des rôles contient les risques : même si une API de ressources est compromise, elle ne peut pas extraire de refresh tokens.
Stockage sécurisé : le débat httpOnly vs localStorage
Le stockage des tokens côté client fait l'objet d'un débat permanent dans la communauté. Chaque approche présente des compromis différents selon le modèle de menace considéré.
localStorage et sessionStorage sont accessibles via JavaScript, ce qui les expose aux attaques XSS (Cross-Site Scripting). Si un attaquant parvient à injecter du code JavaScript dans la page — via une dépendance compromise, une faille dans du contenu utilisateur mal filtré ou une injection dans un script tiers — il peut exfiltrer les tokens stockés. Cette exposition est particulièrement critique pour les refresh tokens à longue durée de vie.
Les cookies httpOnly ne sont pas accessibles via JavaScript, ce qui les protège des attaques XSS. En revanche, ils sont automatiquement envoyés par le navigateur à chaque requête vers le domaine correspondant, ce qui expose au CSRF (Cross-Site Request Forgery). La mitigation nécessite l'utilisation d'un attribut SameSite=Strict ou Lax, combiné à une vérification du header Origin ou à un token CSRF double-submit pour les cas nécessitant SameSite=None.
Le pattern BFF (Backend For Frontend) offre une troisième voie. Un serveur intermédiaire — souvent un composant de l'application elle-même — gère les tokens côté serveur et expose une API de session au frontend. Le frontend ne manipule jamais les tokens directement ; il interagit avec le BFF via des cookies de session sécurisés. Cette architecture déporte la complexité de sécurité des tokens vers un environnement mieux contrôlé et réduit la surface d'attaque côté client.
Un token mal sécurisé offre souvent un accès plus large et plus persistant qu'un mot de passe volé. La durée de vie, le stockage et la révocation ne sont pas des détails d'implémentation : ce sont les fondements de votre posture d'authentification.
Rotation et révocation des tokens
La révocation d'access tokens stateless est structurellement difficile : le serveur n'a pas besoin de les consulter pour les valider, il ne peut donc pas maintenir une liste noire efficacement sans détruire l'avantage de la statelessness. Les approches pratiques incluent la réduction au minimum de la durée de vie des access tokens et la gestion de la révocation au niveau des refresh tokens.
La rotation silencieuse des refresh tokens est le mécanisme recommandé. À chaque utilisation d'un refresh token, le serveur en émet un nouveau et invalide l'ancien. Si un refresh token est utilisé deux fois — signal d'un possible vol — le serveur invalide l'ensemble de la famille de tokens (tous les tokens issus du même refresh token initial) et force une nouvelle authentification. Cette détection de réutilisation (refresh token reuse detection) est une défense efficace contre le vol de refresh tokens.
La token blacklist est une alternative pour les cas nécessitant une révocation immédiate — déconnexion explicite, changement de mot de passe, détection d'une intrusion. Elle implique un stockage central (Redis est le choix habituel pour sa faible latence) consulté à chaque validation. Le coût est une requête supplémentaire par validation ; l'avantage est la révocation instantanée. Pour les access tokens à courte durée de vie, la blacklist peut se limiter aux tokens émis dans les 15 dernières minutes, maintenant la table à une taille raisonnable.
OAuth2 PKCE : le flow recommandé pour les apps publiques
OAuth2 définit plusieurs flows d'autorisation adaptés à différents contextes. Pour les applications publiques — Single Page Applications, applications mobiles, clients desktop — qui ne peuvent pas garder un secret client de manière sécurisée, le flow Authorization Code avec PKCE (Proof Key for Code Exchange) est la seule option recommandée depuis la RFC 9700.
PKCE résout un problème spécifique : protéger l'authorization code contre l'interception. L'application génère un code verifier aléatoire et cryptographiquement fort, calcule son hachage SHA-256 (le code challenge), et envoie ce challenge au serveur d'autorisation avec la demande initiale. Lors de l'échange du code contre un token, l'application présente le verifier original. Le serveur vérifie que le hachage du verifier correspond au challenge qu'il a reçu initialement. Un attaquant ayant intercepté le code d'autorisation ne peut pas l'utiliser sans connaître le verifier original.
Les flows dépréciés à éviter absolument sont l'Implicit Flow — qui transmettait les tokens directement dans le fragment d'URL, exposés aux journaux de serveur et à l'historique du navigateur — et le Resource Owner Password Credentials, qui exigeait que l'application cliente manipule directement les identifiants de l'utilisateur, détruisant la séparation des préoccupations qu'OAuth2 est précisément censé établir. Pour les équipes cherchant une authentification encore plus robuste, l'authentification phishing-résistante avec FIDO2 représente l'évolution naturelle au-delà des tokens.
Pour les APIs machine-to-machine sans utilisateur impliqué, le Client Credentials Flow reste approprié, à condition que le secret client soit stocké de manière sécurisée côté serveur et renouvelé régulièrement. Découvrez comment sécuriser l'authentification de vos APIs au-delà du seul protocole OAuth2, et faites appel à notre expertise en authentification sécurisée pour sécuriser vos architectures. L'utilisation de certificats clients mTLS comme méthode d'authentification du client (private_key_jwt ou tls_client_auth) renforce significativement ce flow dans des environnements à haute sensibilité.