20 sept. 2025·8 min de lecture

Problèmes d’auth JWT dans les prototypes : expiration, refresh, décalage horaire

Les problèmes d’authentification JWT dans les prototypes semblent souvent aléatoires. Apprenez les correctifs pour l’expiration, la rotation des refresh tokens, le décalage horaire et les schémas de stockage sûrs.

Problèmes d’auth JWT dans les prototypes : expiration, refresh, décalage horaire

Pourquoi les tokens JWT cessent de fonctionner de façon aléatoire dans les prototypes

La plupart des problèmes d’authentification JWT dans les prototypes apparaissent de la même manière : une connexion fonctionne, puis les utilisateurs se retrouvent soudainement avec des erreurs 401, renvoyés à l’écran de connexion, ou voient une app qui ne fonctionne qu’après un rafraîchissement.

Ça paraît aléatoire parce que les JWT sont basés sur le temps, votre prototype tient souvent grâce à plusieurs pièces mobiles, et de petites différences s’additionnent. L’horloge d’un portable est quelques minutes déréglée. Un serveur est dans un fuseau horaire différent ou a dérivé. Un onglet du navigateur garde un ancien token et écrase le nouveau. Soudain, la même requête réussit pour vous et échoue pour quelqu’un d’autre.

Un modèle mental simple aide : presque chaque échec d’authent tombe dans l’un de ces trois tiroirs.

  • Temps : expiration (exp), issue-at (iat), décalage horaire, tokens d’accès à courte durée
  • Stockage : token manquant, écrasé, effacé ou stocké au mauvais endroit
  • Validation : mauvaise clé secrète/clé publique, aud/iss incorrects, token révoqué ou roté

Avant de changer du code, capturez les bases. Ça vous évitera des heures de tâtonnements.

  • La réponse exacte en échec (statut + message) et l’endpoint concerné
  • Un horodatage depuis le client et depuis les logs serveur pour la même requête
  • L’heure et le fuseau du dispositif (surtout sur mobile)
  • Les claims du token (exp, iat, iss, aud) et le moment où il a été émis

Exemple concret : un fondateur teste sur son Mac et tout marche. Un utilisateur sous Windows est déconnecté toutes les 10–15 minutes. Le vrai problème n’est pas des « déconnexions aléatoires » — l’horloge du système de l’utilisateur a 6 minutes de retard, le token d’accès expire vite, et le serveur le rejette sans tolérance. Ajoutez un deuxième onglet et vous pouvez aussi avoir un onglet qui rafraîchit pendant que l’autre écrase les tokens stockés.

Si votre prototype a été généré par des outils comme Lovable, Bolt ou Cursor, il est courant de voir un traitement incohérent des tokens entre les pages. FixMyMess trouve souvent un mélange d’expirations courtes, d’absence de logique de refresh, et de stockage non sûr qui se produit simultanément.

Notions de base sur les JWT utiles pour le débogage (sans la théorie inutile)

Un JWT n’est qu’un billet signé. Votre serveur le signe pour pouvoir plus tard vérifier qu’il a bien été émis par vous et n’a pas été modifié. La plupart des JWT ne sont pas chiffrés, donc quiconque possède le token peut le décoder et lire ce qu’il contient.

D’où la première règle pratique : traitez les JWT comme des cartes d’identité lisibles, pas comme des coffres-forts secrets. Si un token fuit (logs, stockage navigateur, captures d’écran, outils d’analytics), tout ce qu’il contient fuit aussi.

Les quelques claims qui expliquent la plupart des échecs

Quand les tokens « cessent de fonctionner de façon aléatoire », c’est généralement un de ces champs, ou la façon dont votre app les vérifie :

  • exp (expires at) : le moment où le token doit être rejeté.
  • iat (issued at) : quand le token a été créé. Souvent utile pour déboguer des problèmes de synchronisation.
  • nbf (not before) : le token doit être rejeté avant ce moment.
  • aud (audience) : à qui le token est destiné (une API spécifique).
  • iss (issuer) : quel système a émis le token.
  • sub (subject) : l’utilisateur ou l’entité que représente le token.

Un décalage courant dans les prototypes : le frontend envoie un token à l’API B qui a été minté pour l’API A (mauvais aud), ou le backend est strict sur le iss dans un environnement mais pas dans un autre.

« Ça échoue » peut signifier deux choses opposées

Les problèmes JWT viennent souvent soit de contrôles manquants, soit de contrôles trop stricts.

Si les contrôles manquent, les tokens peuvent fonctionner alors qu’ils ne devraient pas (faille de sécurité), puis « échouer soudainement » quand vous ajoutez une règle de validation.

Si les contrôles sont trop stricts, les utilisateurs se font expulser pour de petites différences, comme une horloge serveur en avance de 60 secondes (plus loin sur le décalage horaire), ou un token valide mais qui ne correspond pas exactement à la chaîne aud/iss.

Ce qui n’a jamais sa place dans un JWT

Ne mettez pas de secrets ou de données sensibles dans le payload d’un JWT : mots de passe, clés API, identifiants de base de données, codes de réinitialisation, ou données personnelles que vous ne voudriez pas retrouver dans un tableur. Gardez-le minimal : un ID (sub), peut-être un rôle ou quelques permissions, et les claims temporels.

Si vous héritez d’un prototype généré par IA, c’est un problème fréquent que nous voyons chez FixMyMess : des tokens qui incluent par erreur des clés internes ou trop de données utilisateur, puis sont stockés de façon non sûre. Même si l’auth fonctionne, c’est à un fuite près d’un incident sérieux.

Problèmes d’expiration : réglages de exp et iat qui provoquent des déconnexions surprises

La plupart des problèmes JWT dans les prototypes ne sont pas « aléatoires ». Ils sont généralement des calculs d’expiration qui semblent corrects en test local, puis cassent quand de vrais appareils, de vrais réseaux et de petites différences temporelles interviennent.

Quelques erreurs provoquent la plupart des déconnexions surprises :

  • exp trop court. Cinq minutes semblent sûres, mais c’est brutal pour des prototypes avec des cold starts lents, des apps mobile en arrière-plan, ou des connexions instables. Les utilisateurs reviennent après une courte pause et chaque appel échoue.
  • exp manquant. Certaines librairies acceptent des tokens sans expiration, d’autres non, et votre propre code peut les traiter comme expirés. Cela crée des comportements incohérents entre environnements.
  • iat dans le futur. Cela arrive quand l’horloge du serveur est déréglée, que vous utilisez le mauvais fuseau, ou que vous générez des tokens dans un service et les validez dans un autre. Beaucoup de validateurs rejettent les tokens « pas encore valides ».

Un schéma de départ pratique est : token d’accès courte durée + refresh token longue durée. Gardez le token d’accès assez court pour limiter les dégâts en cas de vol, mais assez long pour survivre à l’usage normal. Pour beaucoup de prototypes, 10–20 minutes pour l’accès et plusieurs jours pour le refresh est un bon point de départ. Vous pouvez resserrer après.

Le comportement client compte autant que les réglages de token. Une bonne règle : gérer un 401 une seule fois.

  1. Si une requête retourne 401, appeler le refresh.
  2. Si le refresh réussit, retenter la requête originale une fois.
  3. Si la nouvelle tentative échoue ou si le refresh échoue, déconnecter et afficher un message clair.

Cela évite les boucles de retry infinies et les écrans qui tournent sans fin.

Côté serveur, évitez les erreurs 500 vagues quand un token est expiré. Retournez un 401 clair pour les access tokens expirés ou invalides, et un 401 (ou 403 si vous préférez) quand le refresh token est invalide ou révoqué. Ça permet de distinguer un problème d’expiration d’un vrai crash backend.

Exemple : un fondateur teste sur son laptop et tout marche. Des utilisateurs mobile ouvrent l’app après le déjeuner, le token d’accès a expiré, et l’app continue d’appeler l’API avec l’ancien token jusqu’à abandon. Avec le pattern « 401 -> refresh -> retry une fois », l’utilisateur continue sans s’en apercevoir.

Si vous avez hérité d’un flux d’auth généré par IA où les tokens « échouent parfois », FixMyMess trouve souvent un mélange de exp trop court, de validations incohérentes, et d’absence de logique de refresh au premier audit.

Décalage horaire (clock skew) : quand des tokens corrects échouent parce que l’heure est fausse

Certains problèmes JWT ne proviennent pas d’un mauvais code ni de tokens invalides. Le token peut être correct, signé et inchangé, et échouer parce que l’horloge de l’appareil ou du serveur est fausse.

Le décalage horaire arrive plus souvent dans les prototypes qu’on ne le croit : un téléphone avec l’heure réglée manuellement, une VM qui dérive, un conteneur avec une source temporelle mal configurée, ou deux serveurs du même service décalés d’une minute.

Quand l’heure est fausse, vous voyez en général des échecs autour des claims temporels :

  • nbf : le serveur pense que le token n’est pas encore actif
  • exp : le serveur pense que le token est déjà expiré
  • iat : certaines librairies l’utilisent pour des vérifs supplémentaires et peuvent rejeter des tokens « futurs »

Scénario courant : vous signez un token sur le Serveur A, mais la requête de l’utilisateur est validée sur le Serveur B dont l’horloge a 45 secondes d’avance. Soudain, les utilisateurs sont déconnectés « aléatoirement », ou la connexion fonctionne puis échoue à la page suivante.

Solution pratique : ajouter une petite marge lors de la validation

La plupart des bibliothèques JWT permettent d’autoriser une petite tolérance quand on vérifie exp et nbf. Un bon point de départ est 30 à 120 secondes. Gardez-la petite : la leeway sert à gérer la dérive, pas à prolonger les sessions.

Si vous utilisez une leeway, considérez-la comme un garde-fou, pas comme un pansement. Si vous avez besoin de 10 minutes de tolérance, vous avez probablement un problème de synchronisation temporelle ou de déploiement.

Solution opérationnelle : homogénéiser l’heure partout

La leeway réduit les faux échecs, mais corrigez quand même la cause racine. Contrôles rapides qui détectent la plupart des problèmes :

  • Assurez-vous que tous les serveurs et agents de build se synchronisent sur la même source temporelle (NTP)
  • Évitez de mélanger des hôtes avec l’heure correcte et des conteneurs au temps isolé ou mal réglé
  • Vérifiez que les appareils mobiles de test sont en heure automatique
  • Dans une configuration multi-serveurs, confirmez que les requêtes ne rebondissent pas entre des nœuds avec des temps différents

Si vous héritez d’un prototype généré par IA (commun avec Lovable, Bolt, v0, Cursor, ou Replit), les bugs de clock skew peuvent être masqués par un « ça marche sur ma machine ». Quand FixMyMess audite des échecs d’auth, on trouve souvent qu’une petite tolérance plus une synchronisation horaire correcte suppriment les déconnexions instables sans changer tout le design d’auth.

Refresh tokens : la pièce manquante dans la plupart des flows de prototype

Get an Expert Code Diagnosis
Send your codebase and get a clear list of issues before you commit.

La plupart des problèmes JWT dans les prototypes viennent du fait que l’app essaie d’utiliser un seul token pour tout : un token d’accès court qui doit aussi garder l’utilisateur connecté pendant des jours. Cette tension explique pourquoi les sessions paraissent aléatoires. Un refresh token existe pour maintenir une session vivante sans rendre le token d’accès long (risqué s’il fuit).

Les access tokens doivent être ennuyeux : courte durée, envoyés souvent, faciles à remplacer. Les refresh tokens doivent être rares : utilisés seulement pour obtenir un nouveau access token, et gardés hors des endroits où JavaScript ou les logs peuvent facilement les récupérer.

Erreur courante de prototype : stocker le refresh token de la même façon que l’access token (par exemple dans localStorage) et le traiter comme une simple crédential API. Quand ce token fuit, un attaquant peut générer de nouveaux access tokens jusqu’à ce qu’on s’en aperçoive. Une autre erreur : ne pas avoir du tout de refresh token, donc l’app « résout » les déconnexions en mettant l’expiration du token d’accès énorme. Ça devient vite une dette de sécurité.

Avant de construire le flow, décidez ce que doit signifier « session » pour votre produit :

  • Combien de temps un utilisateur doit rester connecté sans ouvrir l’app ?
  • Fermer le navigateur doit-il déconnecter ou non ?
  • Voulez-vous une session par appareil, ou une connexion sur un appareil doit-elle déconnecter l’autre ?
  • Que se passe-t-il après un changement de mot de passe : déconnecter partout, ou seulement pour les nouvelles sessions ?

L’usage réel ajoute des cas limites que les prototypes gèrent rarement. Les utilisateurs ouvrent plusieurs onglets, les réseaux tombent en cours de requête, et deux requêtes peuvent tenter de rafraîchir en même temps. Sans stratégie, vous obtenez des boucles (refresh, échoue, retry) ou des 401 soudains qui ressemblent à des « problèmes JWT » alors que vos tokens sont corrects.

Si vous avez hérité d’un prototype généré par IA (Lovable, Bolt, v0, Cursor, Replit), cette couche de refresh est souvent manquante ou à moitié câblée. La corriger enlève généralement les bugs « ça marchait hier » en priorité.

Étape par étape : un flow de refresh qui fonctionne avec rotation

La plupart des problèmes JWT apparaissent quand les access tokens expirent et que l’app n’a pas de moyen fiable de récupérer. Un flow de refresh basé sur la rotation résout cela en faisant de l’expiration quelque chose de normal, pas effrayant.

Le flow (login → refresh → retry)

Commencez par mint deux tokens lors de la connexion : un access token à courte durée (minutes) et un refresh token longue durée (jours ou semaines). L’access token est ce que votre API vérifie à chaque requête. Le refresh token sert uniquement à obtenir un nouvel access token.

Une séquence simple et fiable :

  • Le login renvoie access token + refresh token
  • Le client appelle les API avec l’access token jusqu’à ce qu’il échoue en 401
  • Le client appelle l’endpoint de refresh avec le refresh token
  • Le serveur renvoie un nouvel access token et un nouveau refresh token
  • Le client retente l’appel API original une fois

La rotation est le détail clé : à chaque refresh on produit un nouveau refresh token, et l’ancien devient invalide. Ainsi, si un refresh token fuit, il devient inutilisable dès que l’utilisateur réel refresh.

Règles de rotation à implémenter côté serveur

Pour que la rotation des refresh tokens soit réellement sûre (et ne casse pas les utilisateurs), respectez ces règles :

  • Stockez les refresh tokens côté serveur (hachés), avec l’id utilisateur, l’expiration et un identifiant de token unique.
  • Lors du refresh, n’acceptez que l’identifiant de token courant, marquez-le immédiatement comme utilisé/révoqué et émettez un nouvel identifiant.
  • Si un ancien token est présenté à nouveau, considérez-le comme suspect et révoquez toute la session (ou exigez une reconnexion).

La concurrence est là où les prototypes deviennent souvent instables. Deux onglets, un double-clic, ou une tentative de retry mobile peuvent déclencher deux refresh en même temps. Une petite stratégie de grâce évite les déconnexions surprises : autorisez l’usage de l’ancien refresh token une fois de plus pour une courte fenêtre (par exemple 10–30 secondes) s’il vient juste d’être roté, mais seulement si vous pouvez détecter qu’il appartient à la même chaîne de session.

Si vous luttez contre des problèmes JWT qui semblent aléatoires, c’est souvent parce que la rotation, le stockage ou la gestion de la concurrence est à moitié implémentée. Les équipes nous confient souvent un prototype de Lovable/Bolt/v0/Cursor/Replit où le refresh semble « fait » mais casse sous l’usage réel ; nous le durcissons en un flow sûr pour la production rapidement.

Schémas de stockage sûrs (web et mobile)

Fix It in 48-72 Hours
Most projects are completed within 48-72 hours, backed by a 99% success rate.

Si vous voyez des problèmes JWT qui n’apparaissent que hors de votre navigateur, le stockage est souvent la cause. Un prototype peut sembler correct en test, puis commencer à déconnecter les gens « au hasard » quand vous ajoutez de vraies pages, des scripts tiers, ou différents appareils.

Web : traitez le refresh token comme un mot de passe

Pour les applications web, la valeur sûre par défaut est de stocker le refresh token dans un cookie httpOnly et Secure. httpOnly empêche JavaScript de le lire (très utile en cas de bug XSS). Secure garantit qu’il ne circule que sur HTTPS.

Les flags de cookie ne sont pas à laisser au hasard. Faites-en un choix délibéré :

  • httpOnly + Secure : bonne base pour les refresh tokens.
  • SameSite=Lax : fonctionne généralement pour la navigation normale et réduit le risque CSRF.
  • SameSite=None : nécessaire pour des configurations cross-site (domaines différents), mais nécessite Secure et augmente l’exposition au CSRF.
  • Protection CSRF : si l’endpoint de refresh est basé sur cookie, ajoutez des défenses CSRF (par exemple un header CSRF ou un double-submit token).

Évitez de stocker des tokens longue durée dans localStorage ou sessionStorage. C’est pratique, mais si n’importe quel script sur la page peut s’exécuter (XSS, dépendance compromise, extension de navigateur injectée), il peut lire les tokens et les exfiltrer.

Les access tokens sont différents : gardez-les en mémoire si possible, et de courte durée. Si un rafraîchissement de page les fait disparaître, ce n’est pas grave puisque le cookie de refresh peut en générer un nouveau.

Mobile : utilisez le stockage sécurisé, gardez les access tokens courts

Sur iOS et Android, stockez les refresh tokens dans le stockage sécurisé de la plateforme (Keychain sur iOS, Keystore sur Android). Ne mettez pas de refresh tokens dans un stockage applicatif en clair ou dans des logs.

Un schéma pratique :

  • Refresh token : stockage sécurisé, longue durée, rotatif.
  • Access token : uniquement en mémoire, courte durée, renouvelé souvent.
  • En arrière-plan/à la fermeture : lâchez l’access token et récupérez-en un nouveau au resume.

Une source silencieuse de failures : les tokens qui fuient dans des endroits inattendus. Ne mettez jamais d’access tokens dans les URLs (paramètres de requête), et nettoyez-les des logs, events analytics, rapports de crash et popups d’erreur. Par exemple, si votre app loggue la requête complète quand un appel API échoue, elle peut accidentellement capturer l’en-tête Authorization.

Si vous avez hérité d’un flux d’auth IA qui mélange cookies, localStorage et access tokens longue durée, FixMyMess peut l’auditer rapidement et pointer exactement les points de fuite et les choix de stockage non sûrs avant la mise en production.

Pièges courants dans les prototypes qui rendent l’auth instable

Les bugs JWT dans les prototypes paraissent souvent aléatoires parce que le système est à moitié strict et à moitié laxiste. Ça « marche sur votre machine », puis casse après un redeploy, l’ajout d’un second service, ou l’ouverture d’un autre onglet.

Piège 1 : sauter les vérifications iss et aud

Beaucoup de prototypes ne valident que la signature et l’expiration. Ça peut suffire pour un backend unique, mais devient fragile dès que vous ajoutez une seconde API, un worker en arrière-plan, ou un service admin séparé.

Si vous ne validez pas iss et aud, vous pouvez accepter des tokens destinés à un autre service, puis « renforcer la sécurité » plus tard et voir de vrais utilisateurs obtenir des 401 parce que leurs tokens ne correspondent plus aux nouvelles règles.

Façon simple d’éviter les surprises : décidez tôt :

  • Une chaîne issuer pour votre service d’auth
  • Une audience par API (ou une audience partagée si vous n’avez qu’une API)
  • Paramètres cohérents entre dev, staging et prod

Piège 2 : secrets JWT qui changent entre environnements ou lors des redeploys

Les prototypes génèrent souvent des secrets au démarrage, utilisent des .env différents par machine, ou font tourner les clés par accident lors du déploiement. Le résultat ressemble à « les tokens cessent de fonctionner aléatoirement », mais le vrai souci est que le serveur ne peut plus vérifier les tokens qu’il a émis plus tôt.

Si vous redéployez souvent, traitez les clés de signature comme un mot de passe de base de données : gardez-les stables et gérées. Si vous prévoyez de faire une rotation des clés, faites-la délibérément (par ex. en supportant une clé actuelle et une clé précédente pendant une courte période de chevauchement).

Piège 3 : faire confiance au décodage côté client au lieu de la vérification serveur

Décoder un JWT dans le navigateur (ou l’app mobile) n’est pas la même chose que le vérifier. Un prototype peut lire le payload, supposer que l’utilisateur est connecté, et sauter la vérification serveur jusqu’à plus tard.

Cela crée des états confus : l’UI dit « connecté », mais l’API rejette les requêtes. Faites du serveur la source de vérité et considérez que « l’API renvoie 401 » est le signal réel pour refresh ou re-auth.

Piège 4 : cacher l’état auth incorrectement (surtout entre onglets)

Bug courant : un onglet rafraîchit les tokens, un autre onglet continue d’utiliser un ancien access token, et votre app oscille entre fonctionner et échouer. Autre cas : vous cachez un “utilisateur courant” en mémoire et ne le mettez jamais à jour après un refresh.

Si votre app tourne sur plusieurs onglets, décidez comment les mises à jour d’état auth se propagent. Au minimum, gérez proprement les événements « token mis à jour » et « déconnecté » pour ne pas continuer à envoyer des tokens périmés.

Piège 5 : livrer des raccourcis d’auth debug

Le code de prototype accepte parfois des tokens non signés, utilise l’algorithme none, ou contourne la vérification pour les tests. Si cela rejoint la production, vous obtenez à la fois un risque de sécurité et des comportements étranges quand différentes parties du système ne sont pas d’accord sur ce qui est valide.

Si vous avez hérité d’un prototype généré par IA et que l’auth est instable, FixMyMess peut auditer rapidement le code (vérification des tokens, logique de rotation, stockage) et indiquer exactement lesquels de ces pièges vous touchent avant que les utilisateurs ne les rencontrent.

Exemple : ça marche pour vous, mais les utilisateurs sont déconnectés

Make JWT Validation Consistent
Align iss, aud, keys, and leeway across dev, preview, and prod.

Histoire fréquente : vous testez l’app toute la journée sur votre laptop et l’auth a l’air stable. Vous livrez une preview à de vrais utilisateurs et vous recevez des messages du type « je suis déconnecté toutes les 10 minutes » ou « la connexion marche une fois, puis s’arrête aléatoirement ».

Voici un scénario réaliste. Un fondateur construit un prototype avec un outil IA, le lance localement et se connecte dans le même onglet. En production, les utilisateurs ouvrent l’app sur leur téléphone, changent de réseau, mettent l’onglet en arrière-plan, puis reviennent plus tard. C’est exactement là que les problèmes JWT apparaissent : expirations courtes, différences d’horloge, et logique de refresh qui ne marche que sur le chemin heureux.

Comment le reproduire (sans deviner)

Commencez par transformer les « déconnexions aléatoires » en une timeline. Demandez à un utilisateur affecté l’heure à laquelle il s’est connecté et l’heure de la déconnexion (son fuseau compte). Puis récupérez une requête en échec sur le serveur (ou dans les logs) et capturez :

  • L’heure serveur quand la requête a été rejetée (401)
  • Les valeurs exp et iat du token (décodées côté serveur)
  • Si une tentative de refresh a eu lieu, et si elle a échoué
  • Si l’utilisateur avait plusieurs appareils ou onglets ouverts

Si possible, comparez l’horloge serveur à une source de confiance et à votre machine locale. Quelques minutes de dérive suffisent à casser une validation stricte.

Causes les plus probables

Dans les prototypes, ces problèmes reviennent sans cesse :

  • exp trop court (5–15 minutes), et pas de flow de refresh fiable, d’où déconnexions surprises.
  • Échecs JWT liés au décalage horaire : backend vérifie exp et iat sans aucune tolérance, et une machine a l’heure légèrement différente.
  • Bugs dans la rotation du refresh token : vous rotiez les refresh tokens, mais ne gérez pas la « réutilisation » correctement, ou écrasez le refresh token stocké lors d’une course entre deux requêtes.

Chemin de correction pratique

Les corrections sont souvent simples si vous les traitez dans l’ordre.

D’abord, ajoutez une petite leeway à la validation des claims temporels (souvent 30–120 secondes). Ça suffit souvent à arrêter les expirations fausses causées par le décalage horaire.

Ensuite, fiabilisez le refresh. Quand un access token expire, effectuez un refresh unique (une seule tentative) puis rejouez la requête initiale. Si plusieurs requêtes expirent simultanément, faites en sorte qu’une seule rafraîchisse et que les autres attendent le résultat.

Enfin, resserrez le stockage. Utilisez des cookies sécurisés pour les refresh tokens sur le web (httpOnly, Secure, SameSite configuré correctement) et évitez les refresh tokens dans localStorage. Sur mobile, utilisez le stockage sécurisé de la plateforme.

Si votre prototype a été généré rapidement et que l’auth est instable, FixMyMess peut exécuter un audit de code gratuit pour pointer où la logique de refresh, la rotation ou le stockage échouent en usage réel, puis la transformer en un flux prêt pour la production.

Checklist rapide et prochaines étapes

Quand les problèmes JWT semblent « aléatoires », ils ne le sont généralement pas. Un token échoue pour un petit nombre de raisons : les nombres dedans sont faux, l’horloge serveur est déréglée, le flow de refresh est incomplet, ou votre app stocke les tokens de façon fragile.

Commencez par des preuves rapides, pas par des suppositions. Copiez un token en échec, décodez-le, et notez exp, iat, iss, aud et l’id utilisateur (sub). Comparez ensuite avec ce que votre API valide dans cet environnement.

Voici une checklist courte qui attrape la plupart des problèmes de prototype :

  • Décodez un token en échec et confirmez que exp est dans le futur et que iat n’est pas « dans le futur » par rapport au temps du serveur API.
  • Vérifiez l’heure de l’API : contrôlez l’horloge du serveur, le temps des conteneurs, et la synchro d’hébergement. Quelques minutes de dérive suffisent.
  • Confirmez la configuration de signature : assurez-vous que le secret/la clé privée est correct(e) pour cet environnement (dev vs preview vs prod), et que vous ne mélangez pas les clés entre services.
  • Validez audience/issuer : aud et iss doivent correspondre à ce que l’API attend dans chaque environnement (surtout les preview deployments).
  • Testez le comportement de refresh : l’endpoint de refresh renvoie un nouvel access token de manière fiable, et la rotation des refresh tokens n’invalide pas accidentellement des utilisateurs encore actifs.

Après cela, faites un test de bout en bout comme un utilisateur réel : connectez-vous, attendez que le token d’accès expire, puis effectuez un appel API et observez l’app rafraîchir et retenter. Si ça échoue, cherchez ces patterns : refresh token non envoyé (mauvais flags de cookie), refresh token écrasé pendant la rotation, ou client qui ne retente jamais la requête originale.

Le stockage est votre dernier contrôle rapide : évitez les refresh tokens dans localStorage. Pour les apps web, un cookie HTTP-only est généralement le choix le plus sûr par défaut. Pour le mobile, utilisez le stockage sécurisé de la plateforme.

Si votre prototype IA (Lovable, Bolt, v0, Cursor, Replit) a une authent instable et que vous devez la rendre prête pour la production rapidement, FixMyMess peut réaliser un audit de code gratuit pour identifier ce qui casse, puis corriger ou reconstruire le flow en toute sécurité.