Guides JWT

Comment je distingue JWK et JWKS pour vérifier un JWT

Différence entre une JWK et un JWKS, sélection par kid, jwks_uri, rotation de clés et trust path sûr jusqu’à la vérification de signature.

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.

Schéma issuer de confiance, JWKS, kid et JWK
Un issuer de confiance publie le JWKS; kid sélectionne la JWK utilisée pour vérifier la signature.

La différence de base : JWK décrit une clé cryptographique en JSON ; JWKS est un ensemble de JWKs. La différence devient opérationnelle avec kid, rotation, discovery, cache et sélection de clé.

J’utilise JWK Inspector pour une clé et JWKS Validator pour token + key set.

Une JWK représente une clé

{
  "kty": "RSA",
  "kid": "prod-2026-09",
  "use": "sig",
  "alg": "RS256",
  "n": "...",
  "e": "AQAB"
}

Je regarde kty, kid, use, key_ops, alg, puis les paramètres propres au type de clé.

kid est un identifiant de sélection, pas une preuve de confiance.

Un JWKS contient plusieurs JWKs

{
  "keys": [
    { "kty": "RSA", "kid": "old", "alg": "RS256", "use": "sig", "n": "...", "e": "AQAB" },
    { "kty": "RSA", "kid": "new", "alg": "RS256", "use": "sig", "n": "...", "e": "AQAB" }
  ]
}

Le chevauchement old/new est normal pendant une rotation.

Relier kid au JWKS

JWT kid
  ↓
chercher dans JWKS
  ↓
filtrer kty/use/key_ops/alg
  ↓
vérifier la signature

Sans match, je vérifie freshness du JWKS et issuer attendu au lieu de prendre la première clé.

jwks_uri doit appartenir à une chaîne de confiance

expected issuer
  ↓
metadata/discovery de confiance
  ↓
jwks_uri
  ↓
JWKS
  ↓
matching kid
  ↓
signature verification

Je ne fais pas confiance automatiquement à une jku fournie par un token non vérifié.

Compatibilité alg / kty

Je m’attends à des associations cohérentes :

RS256 -> RSA
ES256 -> EC P-256

Je respecte aussi use: sig et key_ops lorsqu’ils existent.

Missing kid, duplicate kid et ambiguïté

Je traite explicitement :

  • kid absent du set ;
  • plusieurs clés avec le même kid ;
  • token sans kid avec plusieurs candidats ;
  • kty/alg incompatible ;
  • ancienne clé retirée trop tôt.

L’ambiguïté est une erreur, pas une raison de tester au hasard.

Vérification Node avec un JWKS distant de confiance

Avec jose, je peux garder l’issuer, l’audience et les algorithmes attendus dans la configuration serveur :

import { createRemoteJWKSet, jwtVerify } from "jose";

const issuer = "https://login.example.com/";
const jwks = createRemoteJWKSet(
  new URL("https://login.example.com/.well-known/jwks.json")
);

const { payload, protectedHeader } = await jwtVerify(token, jwks, {
  issuer,
  audience: "api://orders",
  algorithms: ["RS256"],
});

Le point important n’est pas la bibliothèque : la source du JWKS, l’algorithm allowlist et les claims attendus viennent d’une configuration de confiance.

Comprendre le cache HTTP du JWKS

Un endpoint peut publier des indications comme :

Cache-Control: public, max-age=300
ETag: "jwks-v42"

Je ne télécharge pas le JWKS à chaque requête et je ne le conserve pas indéfiniment. Pour un kid inconnu, un refresh contrôlé peut être pertinent, toujours contre la même source de confiance, avec rate limit pour éviter un fetch storm.

Je distingue :

unknown kid + JWKS stale       -> refresh contrôlé
unknown kid + JWKS fresh       -> token/issuer à examiner
JWKS HTTP 5xx                  -> dépendance temporaire
JWKS timeout                   -> réseau/issuer
kid dupliqué                   -> configuration ambiguë

Observer rotation et sélection en production

Sans stocker les JWT, je peux mesurer :

  • nombre d’unknown_kid par issuer ;
  • refreshes du JWKS ;
  • latence et erreurs du jwks_uri ;
  • kid vus pendant la fenêtre de rotation ;
  • vérifications utilisant encore l’ancienne clé.

Un pic d’unknown_kid après publication d’une nouvelle clé pointe souvent vers cache/propagation. Le même pic sans rotation peut révéler des tokens d’un autre environnement.

Cache et rotation

Télécharger JWKS à chaque requête surcharge l’issuer ; le cacher indéfiniment ignore les nouvelles clés. Un compromis utile est cache + refresh contrôlé sur unknown kid, rate-limité et toujours lié à la même source de confiance.

Voir JWT key rotation pour le chevauchement et le stale cache.

JWK Thumbprint n’est pas kid

RFC 7638 définit un thumbprint calculé de manière canonique. kid est choisi par l’émetteur. Le thumbprint aide à comparer le matériel de clé, mais ne remplace pas la politique d’issuer.

JWK Inspector calcule le thumbprint localement.

Tests négatifs de sélection de clé

kid connu + RS256 + RSA       -> vérifier
kid inconnu                   -> refresh contrôlé ou reject
kid dupliqué                  -> reject pour ambiguïté
kid match + kty incompatible  -> reject
jku arbitraire dans le header -> ne pas changer de trust source
JWKS d’un autre issuer        -> reject même si signature valide

Ces tests évitent une stratégie dangereuse consistant à essayer des clés jusqu’à ce qu’une signature passe.

Matrice JWK vs JWKS

QuestionJWKJWKS
Une clé ?ouiensemble
Tableau keys ?nonoui
Rotation avec chevauchement ?limitéenaturelle
Sélection kid ?optionnellefréquente
Cible de jwks_uri ?nonoui

Mon workflow sûr

  1. Je définis l’expected issuer depuis la configuration.
  2. J’utilise sa metadata/JWKS de confiance.
  3. Je lis alg et kid uniquement comme indices.
  4. Je sélectionne le kid approprié.
  5. Je filtre kty, use, key_ops, alg et curve si nécessaire.
  6. Je rejette absence ou ambiguïté de sélection.
  7. Je vérifie la signature du compact JWT original.
  8. Ensuite je valide iss, aud, exp et les autres claims.
  9. Pour unknown kid, j’analyse cache/rotation sans changer de trust source.
  10. Je corrèle unknown-kid, refresh et erreurs HTTP pour confirmer la cause.

JWKS n’est donc pas seulement « plusieurs JWK » : c’est un élément du cycle de découverte, sélection, cache et rotation des clés.

Sources et références

Guides associés