Guide JSON Web Token: Comment valider un JWT
Guide JSON Web Token: Comment valider un JWT
Apprenez à valider un JWT grâce à ce guide complet. Comprendre la structure d'un JSON Web Token et sa validation à l'aide d'exemples de code pratiques.
- Quickstart
- 15 min
La validation d'un jeton Web JSON (JWT) implique la vérification de sa signature. Elle doit appartenir à la bonne clé publique, ce qui garantit l'authenticité des informations. Elle ne doit pas non plus avoir expiré et doit être envoyée aux bons destinataires.
Comprendre le concept de jeton JWT
Les JSON Web Tokens (JWT) sont une norme largement utilisée pour la transmission sécurisée d'informations entre les différents parties dans les applications Web. Examinons les principaux concepts et composants des JWT, en nous concentrant plus particulièrement sur leur sécurité.
Un JWT est un jeton compact, URL-safe, souvent utilisé dans les applications web pour autoriser l'accès aux ressources, comme les APIs. Il s'agit d'un objet JSON les données sont donc au format JSON et se compose de trois parties :
- Header : Spécifie le type de jeton (
JWT
) et l'algorithme de signature (par exemple,HS256
pour HMAC SHA-256). - Payload (ou claims) : Contient des informations sur l'utilisateur ou le système, connues sous le nom de JWT claims.
- Signature : Valide l'intégrité du jeton, en s'assurant qu'il n'a pas été modifié en cours de route.
En-tête JWT et signature Web JSON (JWS)
L'en-tête JWT spécifie souvent un algorithme de signature (par exemple, RS256
, HS256
), qui est utilisé pour générer la signature JWT. Cette signature fait partie de la norme JSON Web Signature (JWS) et peut être générée à l'aide de clés symétriques (secret partagé) ou asymétriques (paire de clés publique/privée).
Le payload du JSON Web Token : les JWT Claims
Les claims JWT dans le payload stockent des informations telles que :
- sub (sujet) : Identifie l'utilisateur ou l'entité.
- iss (issuer) : Spécifie l'émetteur du jeton (par exemple, le serveur d'autorisation).
- aud (audience) : Indique le destinataire du jeton.
- exp (expiration) : Définit la date d'expiration du jeton.
Voici un exemple de Payload JWT:
{
"aud": [],
"client_id": "d1f1fc75-51a3-4c07-8ba6-698521728290",
"email": "alexandre@cryptr.co",
"env": "sandbox",
"exp": 1709330437,
"iat": 1709294437,
"jti": "0c46d26c-9974-4ee4-96d3-081990e643f9",
"jtt": "access",
"org": "muffun-qy6S9EYuXgtcTeG3YiYwX4",
"scope": ["openid", "email", "profile"],
"sub": "cryptr|41a55520-645d-49eb-a0e8-2e95fde8f56c",
"ver": 3
}
Les claims offrent une certaine souplesse, permettant aux JWT de stocker des données personnalisées ainsi que des noms de claims standard. Les clamins JWT peuvent également être utilisées pour rejeter les jetons qui ne correspondent pas à des critères spécifiques.
Résumé : Meilleures pratiques en matière de sécurité des JWT
Pour utiliser les JWT en toute sécurité, il faut toujours :
- Valider les JWT avant de faire confiance aux informations qu'ils contiennent.
- Vérifier la signature pour s'assurer qu'elle est valide.
- Confirmer que l'émetteur du jeton et le claim de l'audience correspondent aux valeurs attendues.
- Rejeter les jetons s'ils ne répondent pas aux critères de validation.
Cryptr crée des jetons signés avec des clés asymétriques et la vérification des signatures par le biais d'un endpoint JWKS offre une sécurité solide. Dans les API Web, un JWT peut décoder les données en transit tout en garantissant que la signature est valide et appartient à l'émetteur attendu, ce qui en fait un choix fiable pour le contrôle d'accès et l'authentification.
Ces jetons peuvent être déchiffrés à l'aide d'outils en ligne tels que JWT.io ou à l'aide de bibliothèques spécifiques à chaque langue. Cependant, le décodage est assez simple puisqu'il peut être effectué à l'aide d'un simple décodeur Base64.
Maintenant que nous avons vu cela, vous vous demandez peut-être comment s'assurer que le token n'a pas été modifié par une personne malveillante. Tout ce que vous avez à faire, c'est de vérifier votre signature. En effet, les jetons JWT disposent d'un mécanisme de signature. Chaque token est signé par Cryptr lorsqu'il est émis via une clé privée. De votre côté, vous pouvez facilement trouver votre clé publique en utilisant les informations contenues dans le jeton Cryptr déchiffré. Une fois votre clé publique récupérée, vous pouvez alors vérifier la signature de votre token. C'est ce que nous verrons plus en détail dans ce guide.
- Récupérer les jetons d'utilisateur
- Obtenir le jeton dont vous avez besoin
- Décoder le jeton
- Récupérer la clé publique
- Vérifier la signature
1. Récupérer les JWTs des utilisateurs, l'Access token et l'ID Token dans le payload
Pour commencer, vous devrez récupérer les jetons de vos utilisateurs. La plupart du temps, après un challenge d'authentification, vous recevrez un code via les paramètres de la requête HTTP (de notre service à votre URL de redirection).
{your-redirection-url}?code=authorization_code&request_id=request_id
En utilisant ce code, vous pourrez récupérer toutes les informations dont vous avez besoin (ID Token, Access Token, Expiration, etc.). Pour ce faire, vous devez faire une requête API à nos services en utilisant ce code comme paramètre et en spécifiant le grant_type authorization_code
.
Par exemple, la requête ressemblerait à ceci :
Nous devons d'abord obtenir l'ID de la clé (kid) du JWT pour trouver la clé publique associée. Vous pouvez obtenir le kid à partir de l'en-tête du Token (la première partie avant le premier point) avec un simple décodage base64.
curl -X POST '${cryptr_service_url}/oauth/token' \
-d grant_type="authorization_code" \
-d code="9xO5oCjbwHHeIPu8QId3325AAmGjZ76vjD5WA49…"
Vous devriez alors recevoir un payload contenant ce dont vous avez besoin ensuite sous la forme suivante :
{
"access_token": "eyJhbGciO…",
"expires_in": 36000,
"id_token": "eyJhbGciO…",
"scope": [
"openid",
"email",
"profile"
],
"token_type": "Bearer"
}
En fonction de vos besoins, vous pouvez alors récupérer le jeton d'accès, le jeton d'identification ou les deux. En l'état, il ne vous est guère utile puisqu'il est chiffré et donc impossible à comprendre pour un humain. Vous devrez donc le décoder.
2. Décoder les revendications JWT
Une fois votre token récupéré, vous devez le décoder pour pouvoir accéder aux informations qu'il contient de manière lisible. La plupart des langages disposent de bibliothèques ou de fonctions natives permettant de céchiffrer ce jeton qui, rappelons-le, est au format JWT.
Voici une façon de le faire en pseudo-code :
- JavaScript
- Ruby
// npm install jsonwebtoken
const jwt = require('jsonwebtoken');
const token = "YOUR_JWT_TOKEN";
// Decodes without verification
const decoded = jwt.decode(token,
// Setting { complete: true } allows you to access both the header and the payload.
{ complete: true }
);
// You will get the kid (key if of the public key) and the iss (issuer URL)
// usefull for the next step
console.log("Header:", decoded.header);
// {
// "alg": "RS256",
// "iss": "https://auth.cryptr.dev/t/muffun-qy6S9EYuXgtcTeG3YiYwX4",
// "kid": "cbf227bd-1427-44f0-8581-20199f0a8ebb",
// "typ":"JWT"
// }
# gem install jwt
require 'jwt'
# Example JWT token
token = "YOUR_JWT_TOKEN"
# Decode the token without verifying the signature
decoded_token = JWT.decode(token, nil, false)
# The decoded_token array contains [payload, header]
payload = decoded_token[0]
header = decoded_token[1]
# You will get the kid (key if of the public key) and the iss (issuer URL)
# usefull for the next step
puts "Header: #{header}"
puts "Payload: #{payload}"
# {
# "alg": "RS256",
# "iss": "https://auth.cryptr.dev/t/muffun-qy6S9EYuXgtcTeG3YiYwX4",
# "kid": "cbf227bd-1427-44f0-8581-20199f0a8ebb",
# "typ":"JWT"
# }
Il est important de savoir que lorsqu'un serveur reçoit un JWT dans une requête d'autorisation, il doit valider le jeton. Cela permet de s'assurer que le jeton est légitime et qu'il n'a pas été altéré. La validation du JWT comprend généralement les éléments suivants
- Validation de la signature : Le serveur vérifie la signature du jeton en utilisant la clé attendue. Pour les clés asymétriques, le serveur utilise une clé Web JSON publique à partir du point endpoint JSON Web Key Set (JWKS).
- Validation de l'émetteur du jeton : Il vérifie le claim de l'émetteur (
iss
) pour confirmer que le jeton appartient au serveur d'autorisation attendu. - Validation des claims du jeton : Vérifier l'audience (
aud
), l'expiration (exp
) et d'autres claims pour s'assurer que le jeton est valide pour la ressource prévue.
3. Récupérer la clé publique JWKS (JSON Web Key Set) du serveur d'autorisation.
Une fois que vous avez décodé votre token, vous devriez avoir un en-tête similaire à celui-ci :
{
"alg": "RS256",
"iss": "https://auth.cryptr.dev/t/muffun-qy6S9EYuXgtcTeG3YiYwX4",
"kid": "cbf227bd-1427-44f0-8581-20199f0a8ebb",
"typ":"JWT"
}
et un payload similaire à celui-ci:
{
"aud": [],
"client_id": "d1f1fc75-51a3-4c07-8ba6-698521728290",
"email": "alexandre@cryptr.co",
"env": "sandbox",
"exp": 1709330437,
"iat": 1709294437,
"jti": "0c46d26c-9974-4ee4-96d3-081990e643f9",
"jtt": "access",
"org": "muffun-qy6S9EYuXgtcTeG3YiYwX4",
"scope": ["openid", "email", "profile"],
"sub": "cryptr|41a55520-645d-49eb-a0e8-2e95fde8f56c",
"ver": 3
}
Pour récupérer votre clé publique, vous pourrez utiliser le kid contenu dans l'en-tête de votre JWT. Une fois votre kid récupéré, vous devrez utiliser l'endpoint suivant :
{your-cryptr-service-url}/t/{your_org_domain}/.well-known
Pour simplifier, vous pouvez aussi simplement récupérer l'ISS contenu dans l'en-tête de votre jeton et ajouter la route /.well-known.
Si vous préférez cette méthode, vérifiez toujours que le domaine est https://{votre-domaine}.authent.me
pour un serveur dédié ou https://cryptr.eu
(peut aussi être .us
ou .asia
).
- JavaScript
- Ruby
// npm install jwks-rsa
const jwksClient = require('jwks-rsa');
// Initialize the JWKS client
const client = jwksClient({
jwksUri: 'https://YOUR_CRYPTR_BASE_URL/.well-known/jwks.json',
});
// Function to get the public key from the JWKS
const getKey = (header, callback) => {
client.getSigningKey(header.kid, (err, key) => {
if (err) return callback(err);
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
require 'net/http'
require 'uri'
require 'json'
# Configure the JWKS URL from the authorization provider
JWKS_URL = 'https://YOUR_AUTH_PROVIDER/.well-known/jwks.json'
# Method to fetch public keys from the JWKS
def fetch_jwks_keys
uri = URI(JWKS_URL)
response = Net::HTTP.get(uri)
JSON.parse(response)['keys']
end
# Method to find the corresponding key in the JWKS
def find_key(kid, jwks_keys)
jwks_keys.find { |key| key['kid'] == kid }
end
4. Valider le jeton : vérifier la signature du JWT
Lors de la validation manuelle d'un JWT, il est important de :
- Vérifier la signature du jeton pour confirmer qu'il a été émis par une source fiable.
- S'assurer que l'
iss
(émetteur) dans le JWT correspond à l'émetteur du jeton. Si l'iss
ne correspond pas à l'émetteur attendu, rejeter le jeton. - Vérifiez l'
aud
(audience) pour confirmer que le jeton est destiné à la bonne audiences et qu'il est autorisé pour l'application. Si cela ne correspond pas, rejeter le jeton. - Vérifiez l'expiration (
exp
) pour vous assurer que le jeton n'est pas expiré. Si le jeton a expiré, il faut le faire échouer à la validation.
Les middleware JWT peuvent également simplifier la validation dans les frameworks web, en gérant le décodage et la vérification du jeton dans le cadre du cycle de vie de la requête HTTP.
- JavaScript
- Ruby
// npm install jsonwebtoken
const jwt = require('jsonwebtoken');
// Verify the JWT
const validateJwt = (token) => {
jwt.verify(token, getKey, { algorithms: ['RS256'] }, (err, decoded) => {
if (err) {
console.error("Token validation failed:", err);
return;
}
console.log("Token is valid:", decoded);
});
}
// Example usage
const token = "YOUR_JWT_TOKEN";
validateJwt(token);
# gem install jwt
# gem install json-jwt
require 'jwt'
require 'json/jwt'
# JWT validation method
def validate_jwt(token)
jwks_keys = fetch_jwks_keys
decoded_header = JWT.decode(token, nil, false).first
kid = decoded_header['kid']
# Retrieve the matching key
jwk_data = find_key(kid, jwks_keys)
raise "Key not found for KID: #{kid}" if jwk_data.nil?
# Use the public key to verify the JWT
jwk = JSON::JWK.new(jwk_data)
public_key = jwk.to_key
begin
decoded_token = JWT.decode(token, public_key, true, { algorithm: jwk_data['alg'] })
puts "The token is valid: #{decoded_token}"
rescue JWT::DecodeError => e
puts "Token validation error: #{e.message}"
end
end
# Using the method with an example token
token = "YOUR_JWT_TOKEN"
validate_jwt(token)
Sur cet endpoint .well-known
, vous pouvez récupérer la clé publique correspondant à votre kid
. Une fois la clé publique (également appelée JWK) trouvée, il suffit de la récupérer et, dans la plupart des cas, de l'utiliser telle quelle (dans son intégralité) dans les bibliothèques JWT pour vérifier la signature de votre jeton d'accès/d'identification. Certaines bibliothèques acceptent également de transmettre directement l'URL well-known
. Veillez à vérifier les spécificités de chaque bibliothèque pour votre language.
En fonction de la bibliothèque utilisée, il se peut que vous deviez vérifier vous-même la correspondance de votre signature. Dans ce cas, il vous suffira de comparer la signature contenue dans votre JWT avec celle que vous avez obtenue. Si les deux chaînes de caractères sont identiques, félicitations, vous venez de prouver l'authenticité de votre JWT.
Je peux faire confiance à la présence de l'information Token, je peux donc valider :
- la temporalité
- l'expiration,
- l'émetteur : il provient de mon service d'autorisation Cryptr
- et que, en tant qu'application, je suis le bon destinataire (client_id), le bon public.
Pour conclure, voici quelques conseils sur la manière d'utiliser vos jetons.
Identité de l'utilisateur :
- dans le navigateur, l'
id_token
doit être le seul moyen d'authentifier un utilisateur - du côté du serveur, le
user_info
d'unaccess_token
doit être utilisée car nous n'envoyons jamais l'id_token
dans les échanges client/serveur.
Nous espérons que ce petit guide vous a permis de mieux comprendre les JWTs et surtout qu'il vous a aidé à décoder, utiliser et vérifier l'authenticité des tokens que nous vous envoyons.