Vérifier une signature JWT contre un JWKS sans backend

Vérifier une signature JWT contre un JWKS sans backend

Collez un token et un JWKS pour vérifier une signature RS256, ES256 ou HS256 dans le navigateur. Choix par kid, erreurs mesurées, claims à contrôler.

26.10.2026
14 min de lecture
Partager cet article:
JWTjwksoidcSécuritéTutoriel

Vérifier une signature demande le token et la bonne clé publique

Décoder un JWT montre ce qu’il affirme. Le vérifier montre si la clé attendue a signé ces affirmations. Chez un fournisseur OpenID Connect comme Auth0, Keycloak ou Cognito, cette clé se trouve dans un JWKS, un document JSON de clés publiques. Ce guide explique comment le vérificateur choisit une clé et comment lire sa réponse quand une intégration échoue. Il s’appuie sur l’outil JWT verify, qui prend le JWKS sous forme de texte collé et n’ouvre jamais d’adresse, donc sans serveur. Le 26 octobre 2026, nous avons passé 31 cas de test dans le code du vérificateur et nous rapportons chaque résultat. Ils couvrent des tokens RS256, ES256 et HS256, ainsi que des tokens modifiés, expirés ou mal appariés. Les règles viennent des RFC 7515, 7517, 7518, 7519 et 8725. Pour lire un token avant de le vérifier, commencez par notre guide d’encodage et de décodage JWT.

Décoder lit un token. Vérifier contrôle sa signature avec une clé publique du JWKS.
L’en-tête kid désigne la clé. C’est un indice, et il est facultatif (RFC 7515, section 4.1.4).
Un token sans kid a été vérifié avec un JWKS d’une seule clé, et a échoué avec un JWKS de trois clés.
Une signature RS256 d’une clé de 2048 bits pèse 256 octets, une signature ES256 en pèse 64.
Une signature valide ne dit pas que le token est destiné à votre API. Contrôlez aussi iss, aud et exp.
Collez le corps du JWKS, pas son adresse. L’outil ne télécharge rien.

Comment fonctionne un JWKS et comment la clé est choisie

Un JWKS est une liste JSON de clés publiques

La RFC 7517 définit un JWK Set comme un objet avec un tableau keys. L’ordre des clés n’exprime aucune préférence, sauf décision de l’application. Chaque clé indique son type dans kty et porte les nombres nécessaires au contrôle d’une signature.

Améliorations

Les clés RSA portent n et e (module et exposant). Dans notre test, la valeur n d’une clé de 2048 bits comptait 342 caractères Base64URL.
Les clés EC portent crv, x et y. Une clé P-256 produit des signatures ES256.
kid, alg et use sont des étiquettes facultatives. use vaut sig pour signer ou enc pour chiffrer.
Notre jeu de test (deux clés RSA et une clé EC, chacune avec kid, alg et use) faisait 997 caractères : un JWKS collé reste petit.
Une clé de type oct contient un secret partagé dans k. C’est du matériel privé, même dans un document nommé jeu de clés.
Le kid choisit la clé, et ce n’est qu’un indice

La RFC 7515 présente kid comme un indice sur la clé qui a protégé le token, et l’en-tête est facultatif. Le vérificateur le compare au kid de chaque JWK. Le comportement sans correspondance dépend de l’outil, alors nous l’avons testé.

Améliorations

Token avec kid k1 et un jeu de trois clés qui contient k1 : vérifié, et le résultat nomme k1.
Token sans kid et un jeu de trois clés : aucune clé correspondante, car le vérificateur ne peut pas deviner.
Token sans kid et un jeu d’une seule clé, ou un JWK seul : vérifié.
Token avec kid a et une seule clé sans kid : aucune clé correspondante. Une étiquette d’un seul côté ne suffit pas.
Token avec un kid inconnu : aucune clé correspondante. En production, cela signale souvent une rotation des clés chez le fournisseur.
Tailles et formats des signatures

Le troisième segment du token est la signature. Sa longueur et son codage dépendent de l’algorithme, ce qui explique des échecs qui ressemblent à une corruption.

Améliorations

RS256 avec une clé de 2048 bits a donné une signature de 256 octets, d’où le troisième segment très long d’un token RSA.
ES256 a donné 64 octets. La RFC 7518 la définit comme R et S, de 32 octets chacun, mis bout à bout.
La RFC 7518 exige une valeur de 64 octets pour ES256 : une signature DER telle qu’OpenSSL l’affiche n’est donc pas une signature JWS valide avant conversion.
Une signature HS256 fait 32 octets, et la vérifier demande le secret partagé qui l’a produite.
Ce que prouve une signature valide, et ce qu’elle ne prouve pas

Une signature valide montre que le détenteur de la clé privée a signé cet en-tête et ce payload. Elle ne montre pas que le token vise votre service ni qu’il est encore en cours de validité.

Améliorations

Nous avons remplacé sub par admin dans un token valide. Le verdict a été signature invalide.
La RFC 8725, section 3.8, exige que les clés employées appartiennent à l’émetteur nommé dans iss.
Dans notre test, un aud valant other et un iss valant https://evil.example ont échoué comme erreurs de claims, alors que la signature était correcte.
Un token expiré depuis 30 secondes est passé avec une tolérance d’horloge de 60 secondes et a échoué avec une tolérance de 0.

Déboguer un token OIDC sans serveur

Réunir le token et le corps du JWKS

Copiez le token d’accès ou d’identité de la requête qui échoue, sans le préfixe Bearer. Récupérez ensuite le JWKS chez le fournisseur. Son adresse est la valeur jwks_uri du document de découverte, servi à l’adresse de l’émetteur suivie de /.well-known/openid-configuration.

Auth0 publie le jeu à l’adresse https://VOTRE_DOMAINE/.well-known/jwks.json.
Cognito utilise https://cognito-idp.REGION.amazonaws.com/ID_POOL/.well-known/jwks.json et signe ses tokens en RS256.
Keycloak utilise /realms/REALM/protocol/openid-connect/certs. Les versions récentes n’ajoutent plus l’ancien préfixe /auth.
Ouvrez l’adresse dans un navigateur ou avec curl, copiez le corps JSON et collez ce corps, pas l’adresse.
Lisez l’émetteur dans le claim iss du token et prenez l’adresse dans son document de découverte au lieu de l’écrire en dur.
Fixer ce que votre serveur doit contrôler

Les mêmes décisions valent dans l’outil et dans le code de production. Posez-les une fois, dans votre configuration, et ne les lisez jamais dans le token.

N’acceptez que les algorithmes attendus, par exemple RS256. La RFC 8725, section 3.1, range la liste des algorithmes du côté de l’application.
Comparez iss à l’adresse exacte de l’émetteur.
Comparez aud à l’identifiant de votre API.
Admettez une petite tolérance d’horloge. La RFC 7519 parle d’une marge de quelques minutes au plus, et l’outil propose 0, 60 ou 300 secondes.
Rechargez le JWKS mis en cache quand un token porte un kid que vous n’avez jamais vu.

Lire la réponse du vérificateur

Aucune clé correspondante

L’outil répond key_not_found quand le token n’a pas de kid et que le jeu contient plusieurs clés, ou quand son kid est absent du jeu.

Décodez le token dans le décodeur et comparez le kid de son en-tête aux kid du jeu collé. La casse compte.
Avec plusieurs clés et sans kid dans le token, le vérificateur ne peut pas choisir. Collez seulement la clé attendue, ou demandez à l’émetteur d’ajouter kid.
Un kid que vous n’avez jamais rencontré indique peut-être une rotation des clés chez le fournisseur. La documentation de Cognito demande alors de recharger les clés en cache.
Un jeu copié avant la rotation n’a pas la nouvelle clé. Copiez de nouveau le corps chez le fournisseur.
Signature invalide

L’outil a trouvé une clé et le contrôle a échoué. Plusieurs causes donnent le même message.

Un payload modifié échoue. Changer sub dans un token valide a donné signature invalide.
Un token dont le kid désigne la clé B, mais que la clé A a signé, échoue de la même façon. Cherchez deux environnements, comme la préproduction et la production, aux jeux différents.
Un JWK privé a donné signature invalide, avec un message qui exige une clé publique. Collez la partie publique, et vérifiez d’abord le jeu dans l’inspecteur.
Une clé d’un autre type que l’algorithme, par exemple une clé EC avec un token RS256, finit aussi ici.
L’outil refuse l’entrée

Certaines réponses arrivent avant tout contrôle de signature.

Structure JWT invalide : le token porte encore son préfixe Bearer. Dans notre test, l’outil ne l’a pas retiré. Un retour à la ligne en fin de texte n’a rien gêné.
JSON JWK ou JWKS invalide : une adresse collée à la place du corps, une copie tronquée ou un tableau keys vide.
Algorithme non pris en charge pour alg none : un token sans signature ne se vérifie pas, et votre serveur doit aussi le refuser.
Secret requis : le token annonce HS256 et le jeu ne contient que des clés RSA publiques. HMAC demande un secret partagé ou une clé oct.

Quatre erreurs de vérification avec un JWKS

Prendre une signature valide pour une autorisation

La signature prouve qui a émis le token. Elle ne dit rien de ce que son porteur peut faire sur votre service.

Accepter tout token signé par le fournisseur, quelle que soit son audience.
Sauter le contrôle de iss parce que le JWKS vient déjà de l’émetteur.
Lire le résultat vert comme une autorisation.
Un token émis pour une autre API du même fournisseur passe le contrôle de signature et se fait accepter.
Vérifiez ensemble la signature, iss, aud et exp, puis appliquez vos propres règles d’accès.
Coller du matériel de clé privée

Un JWK avec d, ou une clé oct avec k, est un secret. Un jeu de clés publiques n’en a jamais besoin.

Coller dans une messagerie, un ticket ou un outil en ligne un JWK qui contient encore d.
Copier un jeu depuis un coffre de secrets plutôt que depuis le point d’accès public.
Valider dans le dépôt un JWKS de test qui garde ses champs privés.
Notre test avec un JWK RSA privé a échoué uniquement parce que la bibliothèque voulait une clé publique. Le même collage ailleurs pourrait divulguer d.
Ouvrez d’abord le jeu dans l’inspecteur JWKS. Sa vue Inventaire indique hasPrivate pour chaque clé, et la vue formatée retire d, les facteurs RSA et k des clés oct.
Laisser le token choisir l’algorithme

L’en-tête alg appartient à l’expéditeur. Si le vérificateur le suit, c’est l’attaquant qui choisit le contrôle.

Ne passer aucune option algorithms et se fier à l’en-tête alg.
Employer une clé publique RSA comme secret d’un token HS256.
Accepter alg none sur une route de test qui arrive en production.
Le vérificateur de FastMinify reprend l’alg du token pour une clé seule. Dans notre test, une clé étiquetée RS512 a vérifié un token RS256, et une clé marquée use enc l’a vérifié aussi. Le code de production doit être plus strict.
Fixez la liste des algorithmes dans votre configuration et prenez une bibliothèque qui compare alg, use et key_ops, comme le fait jose.
Télécharger le JWKS à chaque requête, ou plus jamais

Les deux extrêmes échouent. L’un ajoute un appel réseau à chaque requête, l’autre bloque les utilisateurs à la première rotation.

Appeler le point d’accès JWKS pour chaque token.
Mettre le jeu en cache pour toujours.
Écrire une clé en dur au lieu du jeu.
jose garde les clés 10 minutes par défaut et ne relit le jeu pour un kid inconnu qu’après un délai de 30 secondes.
Prenez une bibliothèque qui met en cache et recharge pour un kid inconnu, et réglez ses limites en connaissance de cause.

Quatre idées reçues sur les JWKS et les signatures

Le kid prouve quelle clé a signé le token

L’étiquette est écrite par l’expéditeur, elle ne prouve donc rien seule.

Faux. La RFC 7515 parle d’un indice. Dans notre test, un token de kid k2 signé par la clé k1 a renvoyé signature invalide.
Un JWKS est un document secret

On hésite à en coller un dans un outil.

Un jeu de clés publiques est public par conception et chacun peut le télécharger. L’exception est la clé oct, dont la valeur k est un secret partagé à ne jamais publier.
Les signatures ES256 sont en ASN.1 DER, comme celles d’OpenSSL

Cette croyance mène à du code de conversion inutile dans un JWT.

Faux. La RFC 7518 exige une valeur de 64 octets faite de R et S. Notre token ES256 de test avait une signature de 64 octets.
Vérifier un JWT exige un backend

L’idée vient des bibliothèques qui téléchargent les clés et les gardent en cache sur un serveur.

Non. Un JWKS est une donnée publique et la vérification est un calcul local. La page de vérification travaille sur du texte collé dans votre navigateur et ne télécharge aucune clé.

Vérifier et inspecter dans le navigateur

Vérifier un token avec un JWKS

L’outil JWT verify contrôle la signature et, si vous le demandez, l’audience, l’émetteur et les claims de temps. Il gère 14 algorithmes, dont RS256, ES256 et HS256.

1

Collez le token

Collez d’abord le JWT compact. Les onglets de clé suivent l’alg lu dans son en-tête.

2

Collez le corps du JWKS

Collez le JSON du jeu de clés, un JWK seul ou un PEM public. Pour transformer d’abord une clé PEM en JWK, utilisez le convertisseur PEM et JWK. Pour un token HS256, utilisez l’onglet Secret HMAC.

3

Réglez les contrôles

Saisissez l’audience et l’émetteur si vous voulez les comparer, et choisissez une tolérance d’horloge de 0, 60 ou 300 secondes. L’option Ignorer expiration contrôle quand même la signature.

4

Lisez le verdict

Le résultat affiche valide avec l’algorithme et le kid, ou un code d’erreur décrit dans la section de dépannage ci-dessus.

Examiner d’abord le jeu de clés

L’inspecteur JWKS lit un JWK ou un JWKS sans rien vérifier. Utilisez-le avant le vérificateur quand le jeu vient de quelqu’un d’autre.

1

Collez le JWKS

Collez un jeu de clés ou une clé seule. Les champs privés sont retirés de la sortie et de la copie.

2

Passez à la vue Inventaire

L’inventaire liste kid, kty, use, alg, crv et hasPrivate pour chaque clé, sans afficher de matériel de clé.

3

Triez les clés

L’option Trier les clés ordonne le JSON pour comparer deux environnements avec un diff.

Ce que les outils ne font pas

Les deux pages travaillent sur du texte collé. Quelques limites sont utiles à connaître.

Pas de téléchargement d’une adresse JWKS : vous collez le corps.
Pas de tokens chiffrés (JWE) : seulement des tokens signés en trois parties.
L’outil lit l’algorithme dans le token et ne le compare pas à l’alg d’une clé seule.
Vérifié ne veut pas dire fiable : l’outil ne sait pas si l’émetteur mérite votre confiance.
Chaque champ est limité à 512 Ko.

Les mêmes contrôles dans un terminal et dans votre code

Lister les clés d’un fournisseur avec curl et jq

Remplacez l’adresse par celle de votre émetteur. La dernière commande lit le kid dans le premier segment d’un token placé dans la variable TOKEN.

Exemple de base

# Lire le jwks_uri du document de découverte curl -s https://auth.example.com/.well-known/openid-configuration | jq -r .jwks_uri # Lister les clés : kid, type, algorithme et usage curl -s https://auth.example.com/.well-known/jwks.json | jq -c '.keys[] | {kid, kty, alg, use}' # Lire le kid dans l'en-tête du token echo "$TOKEN" | jq -R 'split(".")[0] | gsub("-";"+") | gsub("_";"/") | @base64d | fromjson | .kid'
Vérifier avec jose et un JWKS distant

createRemoteJWKSet télécharge le jeu, le garde en cache et choisit la clé d’après kid et alg. L’algorithme, l’émetteur et l’audience viennent de votre configuration.

Exemple de base

import { createRemoteJWKSet, jwtVerify } from 'jose' const jwks = createRemoteJWKSet(new URL('https://auth.example.com/.well-known/jwks.json')) const { payload, protectedHeader } = await jwtVerify(token, jwks, { algorithms: ['RS256'], // fixé par vous, jamais lu dans le token issuer: 'https://auth.example.com', audience: 'orders-api', clockTolerance: 60, // tolérance, en secondes }) console.log(protectedHeader.kid, payload.sub)
Vérifier avec un JWKS collé, comme le fait l’outil

createLocalJWKSet prend le jeu que vous avez déjà. Dans nos tests, il a levé ERR_JWKS_NO_MATCHING_KEY pour un kid inconnu. Il a levé ERR_JWKS_MULTIPLE_MATCHING_KEYS pour un token sans kid et deux clés RSA. Il a levé ERR_JOSE_ALG_NOT_ALLOWED quand la liste d’algorithmes excluait l’alg du token.

Exemple de base

import { createLocalJWKSet, jwtVerify } from 'jose' // jwksText est le corps JSON copié depuis le fournisseur const jwks = createLocalJWKSet(JSON.parse(jwksText)) try { const { payload, protectedHeader } = await jwtVerify(token, jwks, { algorithms: ['RS256'], issuer: 'https://auth.example.com', audience: 'orders-api', clockTolerance: 60, }) console.log(protectedHeader.kid, payload.sub) } catch (err) { // ERR_JWKS_NO_MATCHING_KEY, ERR_JWS_SIGNATURE_VERIFICATION_FAILED, ERR_JWT_CLAIM_VALIDATION_FAILED, ERR_JWT_EXPIRED console.error(err.code) }

Conclusion

Vérifier avec un JWKS revient à trois décisions. Choisissez la clé par kid et alg. Contrôlez iss, aud et exp. Quand la clé manque, rechargez le jeu au lieu de faire confiance au token. Les outils du navigateur permettent de reproduire chaque cas avec du texte collé avant de toucher à un serveur. Examinez le jeu, vérifiez le token, puis reprenez les mêmes réglages dans votre code.

Essayez les outils de clés dans votre navigateur

Décodez d’abord le token et comparez son kid au jeu avant d’accuser la signature.
Examinez tout JWKS que vous n’avez pas généré, et contrôlez hasPrivate avant de le coller où que ce soit.
Fixez l’algorithme, l’émetteur et l’audience dans votre configuration.
Mettez le JWKS en cache, et rechargez-le quand un token porte un kid inconnu.
Prenez une signature valide pour le premier contrôle, pas pour une autorisation.
Partager cet article
Partager cet article: