Guides JWT

Comment je débogue une erreur JWT 401 Unauthorized

Mon ordre pour JWT 401 : Authorization header, exp/nbf, signature, issuer, audience, token type, scopes et logs serveur.

Par JWTDecoder.toolsDernière vérification le 2026-09-16

Voix éditoriale : la première personne décrit le workflow documenté de JWTDecoder.tools ; JWTDecoder.tools est l’éditeur et l’auteur organisationnel.

Flux de diagnostic JWT 401
Je vérifie header, temps, signature, issuer, audience, type de token et autorisation.

Pour un 401 Unauthorized avec JWT, je parcours la chaîne d’authentification dans l’ordre : transport HTTP, temps, signature, issuer, audience, type de token, puis seulement permissions.

Avec Expected Issuer/Audience connus, je commence par JWT Debugger.

1. Vérifier l’Authorization header

Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

Je cherche :

  • header absent ;
  • Bearer incorrect ;
  • token entre guillemets ;
  • reverse proxy qui retire Authorization ;
  • token ancien en cache ;
  • ID Token envoyé à la place de l’Access Token.

Un bon JWT ne sert à rien s’il n’atteint jamais la couche de validation.

2. Lire WWW-Authenticate et les logs

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token"

Je cherche ensuite un détail plus précis :

token expired
invalid signature
invalid issuer
invalid audience
kid not found
token not active

invalid_token seul ne suffit pas à identifier la cause. Je garde aussi un request ID, la route, la méthode, l’issuer attendu et la catégorie de rejet, jamais le JWT complet.

3. Vérifier exp, nbf et l’horloge serveur

Avec JWT Expiry Checker :

now < nbf   => pas encore actif
now >= exp  => expiré

Je contrôle aussi seconds/milliseconds et clock skew, sur l’horloge du composant qui rejette réellement la requête.

4. Vérifier signature et algorithme

Je contrôle :

  • alg autorisé ;
  • secret/public key de confiance ;
  • kid ;
  • JWKS/cache ;
  • key rotation ;
  • octets header.payload originaux.

J’utilise JWT Signature Verifier ou JWKS Validator.

5. Comparer iss exactement

expected: https://login.example.com/prod
actual:   https://login.example.com/dev

En cas de mismatch, je vérifie tenant, discovery et source de clés.

6. Vérifier aud

{ "aud": ["api://billing", "api://reporting"] }

Si Orders attend api://orders, le rejet est normal. Le guide audience mismatch couvre tableaux, environnements et ID/Access Token.

7. Distinguer 401 et 403

StatutHypothèse de départ
401token/identité non acceptés
403identité acceptée mais permission insuffisante

Cela évite de déboguer les rôles avant d’avoir une authentification valide.

8. Instrumenter la middleware sans journaliser les credentials

Avec Express, je préfère loguer l’étape du rejet :

app.use(async (req, res, next) => {
  const auth = req.get("authorization");
  if (!auth?.startsWith("Bearer ")) {
    console.warn("auth_fail", { stage: "missing_bearer", requestId: req.id });
    return res.status(401).end();
  }

  try {
    req.auth = await verifyAccessToken(auth.slice(7));
    next();
  } catch (error) {
    console.warn("auth_fail", {
      stage: classifyJwtError(error),
      requestId: req.id,
    });
    res.status(401).end();
  }
});

Je distingue au minimum expired, not_active, bad_signature, unknown_kid, issuer_mismatch, audience_mismatch et wrong_token_type.

9. Vérifier proxy/gateway avec les headers réellement transmis

Quand le token fonctionne en local mais pas derrière Nginx/CDN/API Gateway, je compare l’entrée et la sortie du proxy. Un exemple de configuration qui conserve le header :

proxy_set_header Authorization $http_authorization;
proxy_set_header X-Request-ID $request_id;

Je ne suppose pas que cette ligne est nécessaire partout : je vérifie le comportement réel. Je cherche aussi une double validation, un issuer différent au gateway ou une audience différente entre edge et backend.

10. Reproduire avec une requête minimale

curl -i https://api.example.com/orders \
  -H 'Authorization: Bearer eyJ...'

Je garde le status, WWW-Authenticate, le request ID et l’heure UTC. Si le même token fonctionne sur un autre endpoint, je compare resource/audience/policy au lieu de régénérer le token au hasard.

Ce que je mesure en production

Sans stocker les JWT, je peux suivre :

  • 401 par route ;
  • 401 par catégorie de validation ;
  • unknown_kid par issuer ;
  • expirés vs mauvaises signatures ;
  • échecs/latence du refresh JWKS ;
  • évolution des 401 pendant une rotation de clés.

Un pic d’unknown_kid après rotation pointe vers cache/JWKS. Un pic d’audience_mismatch après déploiement d’une nouvelle API pointe plutôt vers acquisition/configuration du token.

Tests négatifs avant déploiement

Authorization absent               -> 401
Bearer vide                        -> 401
token expiré                       -> 401
nbf futur                          -> 401
signature modifiée                 -> 401
issuer incorrect                   -> 401
audience incorrecte                -> 401
token valide sans permission       -> 403 ou équivalent policy

Ils empêchent qu’un « correctif » consiste à désactiver des validations jusqu’à faire passer le happy path.

Arbre de décision

Authorization présent ?
  non -> client/proxy
  oui
   ↓
temps valide ?
  non -> refresh/clock
  oui
   ↓
signature valide avec key de confiance ?
  non -> alg/kid/key/JWKS
  oui
   ↓
iss correct ?
  non -> issuer/tenant/environment
  oui
   ↓
aud correct ?
  non -> resource/token acquisition
  oui
   ↓
token type + permissions

Mon checklist 401

  1. Je reproduis la requête et garde status, response headers, request ID et logs.
  2. Je confirme Authorization: Bearer à l’entrée de l’API.
  3. Je décode le token original avec JWT Decoder.
  4. Je vérifie exp, nbf et server clock.
  5. Je vérifie algorithme et signature.
  6. Je compare iss à la configuration.
  7. Je compare aud à l’API attendue.
  8. Je confirme Access Token vs ID Token.
  9. Je compare edge/gateway et application si le problème n’existe que derrière proxy.
  10. Ensuite seulement je vérifie scopes/roles/policies.
  11. Je corrige la couche fautive au lieu de désactiver une validation.

Le 401 devient ainsi une suite d’hypothèses vérifiables au lieu d’un message opaque.

Sources et références

Guides associés