JWT Decoder Python — Décoder des JWT avec PyJWT
Utilisez le Décodeur JWT gratuit directement dans votre navigateur — sans installation.
Essayer Décodeur JWT en ligne →Toute API utilisant l'authentification par token vous remet un JWTà un moment ou un autre, et comprendre ce qu'il contient est une tâche qui revient constamment en développement. Un décodeur JWT en Pythonprend cette chaîne base64 opaque et la transforme en un dictionnaire de claims lisible avec lequel vous pouvez réellement travailler. Le paquet PyPI qu'il vous faut est PyJWT — installé avec pip install PyJWT mais importé via import jwt. Ce guide passe en revue jwt.decode() avec vérification complète de la signature, le décodage sans secret pour une inspection rapide, le décodage manuel en base64 sans bibliothèque, la vérification de clé publique RS256, et les pièges courants rencontrés dans des systèmes d'authentification en production. Pour une vérification ponctuelle, le décodeur JWT en ligne fait cela instantanément sans aucun code. Tous les exemples ciblent Python 3.10+ et PyJWT 2.x.
- ✓pip install PyJWT, puis import jwt — le nom du paquet et le nom d'import sont différents, ce qui piège presque tout le monde.
- ✓jwt.decode(token, key, algorithms=["HS256"]) retourne un dict simple avec les claims. Passez toujours algorithms explicitement.
- ✓Pour inspecter les claims sans vérification : jwt.decode(token, options={"verify_signature": False}, algorithms=["HS256"]).
- ✓Pour les tokens RSA/EC : pip install PyJWT cryptography — le backend cryptography est requis pour les algorithmes asymétriques.
- ✓Le décodage manuel (base64 + json) fonctionne sans bibliothèque mais ignore toute validation de signature et d'expiration.
Qu'est-ce que le décodage JWT ?
Un JSON Web Token est composé de trois segments encodés en base64url séparés par des points : un en-tête (algorithme et type de token), un payload (les claims — identifiant utilisateur, rôles, date d'expiration), et une signature. Décoder un JWT signifie extraire les segments d'en-tête et de payload, les décoder en base64url, et parser le JSON résultant en un dictionnaire de claims.
L'en-tête indique quel algorithme a été utilisé pour signer le token et inclut parfois un kid (identifiant de clé) pour trouver la bonne clé de vérification. Le payload contient les données réelles : à qui le token a été émis (sub), quand il expire (exp), pour quel service il est destiné (aud), ainsi que tout claim personnalisé défini par votre application. Le segment de signature prouve que le token n'a pas été altéré, mais vous avez besoin de la clé secrète ou de la clé publique pour le vérifier. Décodage et vérification sont deux opérations distinctes. Vous pouvez décoder le payload sans vérifier la signature (utile pour le débogage), mais ne faites jamais confiance à des claims non vérifiés pour des décisions d'autorisation.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxMTgxNTYwMH0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
{
"sub": "usr_8f2a",
"role": "admin",
"exp": 1711815600
}jwt.decode() — Décoder et vérifier avec PyJWT
jwt.decode() est la fonction principale de la bibliothèque PyJWT. Elle prend la chaîne du token encodé, la clé secrète (pour les algorithmes HMAC) ou la clé publique (pour RSA/EC), et une liste algorithms obligatoire. La fonction vérifie la signature, contrôle les claims standard comme exp et nbf, et retourne le payload sous forme de dictionnaire Python. En cas d'échec — mauvaise signature, token expiré, mauvais algorithme — elle lève une exception spécifique.
Exemple minimal fonctionnel
import jwt
# A shared secret between the issuer and this service
SECRET_KEY = "k8s-webhook-signing-secret-2026"
# Encode a token first (simulating what an auth server would issue)
token = jwt.encode(
{"sub": "usr_8f2a", "role": "admin", "team": "platform"},
SECRET_KEY,
algorithm="HS256"
)
print(token)
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3Jf...
# Decode and verify the token
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
print(payload)
# {'sub': 'usr_8f2a', 'role': 'admin', 'team': 'platform'}
print(payload["role"])
# adminLe paramètre algorithms est une liste, pas une simple chaîne, et il est obligatoire dans PyJWT 2.x. C'est une mesure de sécurité : sans elle, un attaquant pourrait forger un token avec alg: none dans l'en-tête et contourner entièrement la vérification. Spécifiez toujours exactement les algorithmes que votre application accepte. Si vous n'émettez que des tokens HS256, la liste doit être ["HS256"] — pas ["HS256", "RS256", "none"]. Restreindre la liste réduit la surface d'attaque.
Un point qui m'a perturbé au début : PyJWT 2.x a modifié jwt.encode() pour retourner une chaîne au lieu d'octets. Si vous lisez de vieilles réponses Stack Overflow qui appellent .decode("utf-8") sur le token encodé, ce code est de l'ère PyJWT 1.x et lèvera une AttributeError sur la version 2.x. Le token est déjà une chaîne — utilisez-le directement.
Cycle complet avec expiration
import jwt
from datetime import datetime, timedelta, timezone
SECRET_KEY = "webhook-processor-secret"
# Create a token that expires in 1 hour
payload = {
"sub": "svc_payment_processor",
"iss": "auth.internal.example.com",
"aud": "https://api.example.com",
"exp": datetime.now(timezone.utc) + timedelta(hours=1),
"permissions": ["orders:read", "refunds:create"],
}
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
# Later, when the token arrives in a request header:
try:
decoded = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"],
audience="https://api.example.com",
issuer="auth.internal.example.com",
)
print(f"Service: {decoded['sub']}")
print(f"Permissions: {decoded['permissions']}")
except jwt.ExpiredSignatureError:
print("Token expired — request re-authentication")
except jwt.InvalidAudienceError:
print("Token not intended for this API")
except jwt.InvalidIssuerError:
print("Token issued by unknown authority")datetimeen timestamps Unix lors de l'encodage. Au décodage, les claims exp, iat, et nbfsont retournés sous forme d'entiers, pas d'objets datetime. Vous devez les convertir vous-même avec datetime.fromtimestamp(payload["exp"], tz=timezone.utc).Décoder un JWT sans vérification de signature
Parfois vous devez lire les claims avant de pouvoir vérifier le token. Un scénario courant : l'en-tête du token contient un champ kid (identifiant de clé), et vous devez récupérer la clé publique correspondante depuis un endpoint JWKS avant de pouvoir vérifier. PyJWT prend en charge cela avec l'option verify_signature: False. Vous passez quand même la liste algorithms, mais l'argument key est ignoré.
import jwt
token = (
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNpZy0xNzI2In0"
".eyJzdWIiOiJ1c3JfM2M3ZiIsInNjb3BlIjoicmVhZDpvcmRlcnMiLCJpc3MiOiJhdXRoLmV4YW1wbGUuY29tIn0"
".signature_placeholder"
)
# Step 1: Read claims without verification to get routing info
unverified = jwt.decode(
token,
options={"verify_signature": False},
algorithms=["RS256"]
)
print(unverified)
# {'sub': 'usr_3c7f', 'scope': 'read:orders', 'iss': 'auth.example.com'}
# Step 2: Read the header to find which key to use
header = jwt.get_unverified_header(token)
print(header)
# {'alg': 'RS256', 'typ': 'JWT', 'kid': 'sig-1726'}
# Now use header['kid'] to fetch the correct public key from your JWKS endpointIl y a une distinction subtile ici. jwt.get_unverified_header() lit uniquement l'en-tête — le premier segment. L'appel jwt.decode() avec verify_signature: False lit le payload (deuxième segment). Combinés, vous pouvez tout extraire d'un token sans clé. PyJWT valide quand même que le token a la bonne structure (trois segments séparés par des points, base64 valide, JSON valide) même lorsque la vérification de signature est désactivée. Si le token est structurellement malformé, il lève DecodeError indépendamment des options que vous passez.
Référence des paramètres jwt.decode()
La signature complète est jwt.decode(jwt, key, algorithms, options, audience, issuer, leeway, require). Tous les paramètres après algorithms sont des arguments nommés uniquement.
Le dict options offre un contrôle fin sur les validations effectuées par PyJWT. Les clés correspondent à des vérifications individuelles : verify_signature, verify_exp, verify_nbf, verify_iss, verify_aud, et verify_iat. Toutes sont à True par défaut, sauf si vous les définissez explicitement à False. En production, laissez-les toutes à leurs valeurs par défaut. Je ne désactive des vérifications individuelles qu'en développement, quand je travaille avec de vieux tokens de test et que j'ai besoin de contourner temporairement l'expiration.
import jwt
# Require specific claims to be present — raises MissingRequiredClaimError if absent
payload = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"],
options={"require": ["exp", "iss", "sub"]},
issuer="auth.internal.example.com",
)
# During development only: skip expiry to test with old tokens
dev_payload = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"],
options={"verify_exp": False}, # DO NOT use in production
)Décodage manuel d'un JWT avec base64 et json
Vous pouvez décoder un payload JWT en n'utilisant que la bibliothèque standard Python — sans pip install nécessaire. C'est réellement utile dans plusieurs situations : des scripts de débogage où ajouter une dépendance serait excessif, des environnements CI restreints où seule la bibliothèque standard est disponible, des fonctions AWS Lambda où vous souhaitez minimiser le temps de démarrage à froid, ou simplement pour comprendre ce qu'est réellement un JWT. Le processus est simple : découpez sur les points, prenez le segment voulu, ajoutez le padding base64, décodez et parsez le JSON.
Les modules base64 et json font tous deux partie de la bibliothèque standard Python, donc cette approche fonctionne sur toute installation Python à partir de la 3.6. Les fonctions ci-dessous traitent séparément l'en-tête (premier segment) et le payload (deuxième segment) :
import base64
import json
def decode_jwt_payload(token: str) -> dict:
"""Decode the JWT payload without signature verification.
Works with any JWT — HS256, RS256, ES256, etc.
"""
parts = token.split(".")
if len(parts) != 3:
raise ValueError(f"Expected 3 JWT segments, got {len(parts)}")
payload_b64 = parts[1]
# base64url uses - and _ instead of + and /
# Python's urlsafe_b64decode handles this, but needs padding
payload_b64 += "=" * (-len(payload_b64) % 4)
payload_bytes = base64.urlsafe_b64decode(payload_b64)
return json.loads(payload_bytes)
def decode_jwt_header(token: str) -> dict:
"""Decode the JWT header (algorithm, key ID, type)."""
header_b64 = token.split(".")[0]
header_b64 += "=" * (-len(header_b64) % 4)
return json.loads(base64.urlsafe_b64decode(header_b64))
# Example usage
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxMTgxNTYwMH0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
header = decode_jwt_header(token)
print(f"Algorithm: {header['alg']}")
# Algorithm: HS256
claims = decode_jwt_payload(token)
print(f"Subject: {claims['sub']}")
print(f"Role: {claims['role']}")
# Subject: usr_8f2a
# Role: adminL'astuce du padding (+= "=" * (-len(s) % 4)) est ce que tout le monde oublie. Le base64url des JWT omet les caractères = de fin, mais la fonction urlsafe_b64decode de Python les requiert. Sans la correction du padding, vous obtenez une binascii.Error: Incorrect padding.
jwt.decode() avec une vraie clé.Décoder des JWTs depuis des réponses API et des fichiers
Les deux scénarios réels les plus courants : extraire un JWT d'une réponse HTTP (un endpoint de token OAuth, une API de connexion) et lire des tokens depuis des fichiers (identifiants de compte de service, secrets montés dans Kubernetes, tokens mis en cache sur disque). Les deux nécessitent une gestion des erreurs appropriée. Les requêtes réseau échouent. Les fichiers disparaissent. Les tokens expirent entre le moment où ils ont été mis en cache et celui où ils sont lus.
Les exemples ci-dessous utilisent httpx pour les appels HTTP (remplacez par requests si vous préférez, le modèle est identique) et pathlib.Path pour les opérations sur les fichiers. Chaque exemple capture des exceptions PyJWT spécifiques plutôt qu'un simple except Exception, afin de répondre de manière appropriée à chaque mode d'échec : ré-authentifier à l'expiration, alerter en cas d'échec de signature, réessayer en cas de timeout réseau.
Décoder un JWT depuis une réponse API
import jwt
import httpx # or requests
TOKEN_ENDPOINT = "https://auth.example.com/oauth/token"
SECRET_KEY = "shared-webhook-signing-key"
def get_and_decode_token() -> dict:
"""Fetch an access token from the auth server and decode it."""
try:
response = httpx.post(
TOKEN_ENDPOINT,
data={
"grant_type": "client_credentials",
"client_id": "svc_order_processor",
"client_secret": "cs_9f3a7b2e",
},
timeout=10.0,
)
response.raise_for_status()
except httpx.HTTPError as exc:
raise RuntimeError(f"Token request failed: {exc}") from exc
token_data = response.json()
access_token = token_data["access_token"]
try:
payload = jwt.decode(
access_token,
SECRET_KEY,
algorithms=["HS256"],
audience="https://api.example.com",
)
return payload
except jwt.InvalidTokenError as exc:
raise RuntimeError(f"Invalid token from auth server: {exc}") from exc
claims = get_and_decode_token()
print(f"Service: {claims['sub']}, Scopes: {claims.get('scope', 'none')}")Décoder un JWT depuis un fichier
import jwt
from pathlib import Path
from datetime import datetime, timezone
TOKEN_PATH = Path("/var/run/secrets/service-account-token")
PUBLIC_KEY_PATH = Path("/etc/ssl/auth/public_key.pem")
def decode_token_from_file() -> dict:
"""Read a JWT from a file, verify with a PEM public key."""
try:
token = TOKEN_PATH.read_text().strip()
public_key = PUBLIC_KEY_PATH.read_text()
except FileNotFoundError as exc:
raise RuntimeError(f"Missing file: {exc.filename}") from exc
try:
payload = jwt.decode(
token,
public_key,
algorithms=["RS256"],
audience="https://internal-api.example.com",
)
except jwt.ExpiredSignatureError:
exp_time = jwt.decode(
token,
options={"verify_signature": False},
algorithms=["RS256"],
).get("exp", 0)
expired_at = datetime.fromtimestamp(exp_time, tz=timezone.utc)
raise RuntimeError(f"Token expired at {expired_at.isoformat()}")
except jwt.InvalidTokenError as exc:
raise RuntimeError(f"Token verification failed: {exc}") from exc
return payload
claims = decode_token_from_file()
print(f"Subject: {claims['sub']}, Issuer: {claims['iss']}")Décodage JWT en ligne de commande
Parfois vous avez juste besoin d'inspecter un token depuis le terminal sans écrire de script. Peut-être déboguez-vous un flux OAuth et voulez voir ce qui se trouve dans l'en-tête Authorization, ou vous avez récupéré un token dans les DevTools du navigateur et voulez vérifier son expiration. Le flag -c de Python en fait une commande d'une seule ligne. Envoyez le token via pipe et obtenez les claims en JSON formaté. Pas besoin de fichier script, pas besoin d'environnement virtuel.
# Decode JWT payload from clipboard or variable (no verification)
echo "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiJ9.sig" \
| python3 -c "
import sys, base64, json
token = sys.stdin.read().strip()
payload = token.split('.')[1]
payload += '=' * (-len(payload) % 4)
print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))
"
# {
# "sub": "usr_8f2a",
# "role": "admin"
# }# Decode JWT header to check algorithm and key ID
echo "eyJhbGciOiJSUzI1NiIsImtpZCI6InNpZy0xNzI2In0.payload.sig" \
| python3 -c "
import sys, base64, json
token = sys.stdin.read().strip()
header = token.split('.')[0]
header += '=' * (-len(header) % 4)
print(json.dumps(json.loads(base64.urlsafe_b64decode(header)), indent=2))
"
# {
# "alg": "RS256",
# "kid": "sig-1726"
# }# If PyJWT is installed, verify and decode in one step
python3 -c "
import jwt, sys, json
token = sys.argv[1]
payload = jwt.decode(token, options={'verify_signature': False}, algorithms=['HS256'])
print(json.dumps(payload, indent=2))
" "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOGYyYSJ9.sig"Pour une alternative visuelle sans configuration de terminal, collez votre token dans le décodeur JWT ToolDecket visualisez instantanément l'en-tête, le payload et le statut de vérification de la signature.
python-jose et autres alternatives
python-jose est une bibliothèque JWT alternative qui prend en charge nativement JWS, JWE (tokens chiffrés) et JWK. Si votre application doit gérer des JWTs chiffrés (JWE) — où le payload lui-même est chiffré, pas seulement signé — python-jose est le bon choix car PyJWT ne prend pas en charge JWE du tout. La bibliothèque dispose également d'une gestion native des ensembles de clés JWKS, ce qui simplifie l'intégration avec des fournisseurs d'identité comme Auth0, Okta ou Keycloak qui exposent des ensembles de clés rotatifs. L'interface de décodage est presque identique à PyJWT, donc passer de l'un à l'autre nécessite des changements de code minimaux :
# pip install python-jose[cryptography]
from jose import jwt as jose_jwt
token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInNjb3BlIjoib3JkZXJzOnJlYWQifQ.signature"
# Verified decode — same pattern as PyJWT
payload = jose_jwt.decode(
token,
"signing-secret-key",
algorithms=["HS256"],
audience="https://api.example.com",
)
print(payload)
# {'sub': 'usr_8f2a', 'scope': 'orders:read'}
# Unverified decode
claims = jose_jwt.get_unverified_claims(token)
header = jose_jwt.get_unverified_header(token)
print(f"Algorithm: {header['alg']}, Subject: {claims['sub']}")Ma recommandation : commencez par PyJWT. Il couvre 95 % des cas d'usage JWT, dispose de la plus grande communauté, et l'API est propre. Passez à python-jose si vous avez besoin du support JWE ou préférez sa gestion JWKS. Une troisième option qui mérite d'être mentionnée est Authlib, qui intègre la gestion JWT au sein d'un framework OAuth/OIDC beaucoup plus large. Si vous utilisez déjà Authlib pour des flux client OAuth, son module authlib.jose.jwt vous évite d'ajouter une deuxième dépendance JWT. Sinon, c'est une dépendance lourde juste pour le décodage de tokens.
Sortie terminal avec coloration syntaxique
Lire des claims JWT bruts dans un terminal est suffisant pour des vérifications rapides, mais quand vous déboguez des payloads de tokens régulièrement (je le faisais quotidiennement en construisant une passerelle d'authentification interne), une sortie colorisée fait vraiment la différence. Les valeurs de type chaîne, nombre, booléen et null s'affichent en couleurs distinctes, ce qui vous permet de repérer en un coup d'œil une permission manquante ou un timestamp d'expiration incorrect sans lire chaque caractère.
La bibliothèque rich (pip install rich) dispose d'une fonction print_json qui prend soit une chaîne JSON soit un dict Python et l'affiche avec une coloration syntaxique complète dans le terminal. Combinez-la avec PyJWT pour un workflow d'inspection JWT en deux lignes :
# pip install rich PyJWT
import jwt
from rich import print_json
token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiIsInBlcm1pc3Npb25zIjpbIm9yZGVyczpyZWFkIiwicmVmdW5kczpjcmVhdGUiXSwiZXhwIjoxNzExODE1NjAwfQ.sig"
payload = jwt.decode(
token,
options={"verify_signature": False},
algorithms=["HS256"]
)
# Colorized, indented JSON output in the terminal
print_json(data=payload)
# {
# "sub": "usr_8f2a", ← strings in green
# "role": "admin",
# "permissions": [
# "orders:read",
# "refunds:create"
# ],
# "exp": 1711815600 ← numbers in cyan
# }richcontient des codes d'échappement ANSI. Ne l'écrivez pas dans des fichiers ni ne la retournez depuis des endpoints API — elle est réservée à l'affichage terminal. Utilisez json.dumps()quand vous avez besoin d'une sortie en texte brut.Traitement de grands volumes de tokens
Les tokens JWT eux-mêmes sont petits (généralement moins de 2 Ko chacun), mais il existe des scénarios où vous les traitez en masse. Analyse des journaux d'audit après un incident de sécurité. Scripts de migration de sessions lors d'un changement de fournisseur d'authentification. Validation par lot pour la conformité où vous devez prouver que chaque token émis au cours des 90 derniers jours a été signé avec la bonne clé. Si vous avez des dizaines de milliers de tokens dans un fichier journal NDJSON, le traitement ligne par ligne évite de charger l'intégralité du fichier en mémoire et vous permet de rapporter les résultats de manière incrémentale.
Valider des tokens par lot depuis un journal d'audit
import jwt
import json
from pathlib import Path
SECRET_KEY = "audit-log-signing-key"
def validate_token_log(log_path: str) -> dict:
"""Process an NDJSON file where each line has a 'token' field.
Returns counts of valid, expired, and invalid tokens.
"""
stats = {"valid": 0, "expired": 0, "invalid": 0}
with open(log_path) as fh:
for line_num, line in enumerate(fh, 1):
line = line.strip()
if not line:
continue
try:
record = json.loads(line)
token = record["token"]
except (json.JSONDecodeError, KeyError):
stats["invalid"] += 1
continue
try:
jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
stats["valid"] += 1
except jwt.ExpiredSignatureError:
stats["expired"] += 1
except jwt.InvalidTokenError:
stats["invalid"] += 1
return stats
result = validate_token_log("auth-events-2026-03.ndjson")
print(f"Valid: {result['valid']}, Expired: {result['expired']}, Invalid: {result['invalid']}")
# Valid: 14832, Expired: 291, Invalid: 17Extraire des claims depuis un export NDJSON de tokens
import base64
import json
from datetime import datetime, timezone
def extract_claims_stream(input_path: str, output_path: str):
"""Read tokens line by line, decode payloads, write flattened claims."""
with open(input_path) as infile, open(output_path, "w") as outfile:
for line in infile:
line = line.strip()
if not line:
continue
record = json.loads(line)
token = record.get("access_token", "")
parts = token.split(".")
if len(parts) != 3:
continue
payload_b64 = parts[1] + "=" * (-len(parts[1]) % 4)
claims = json.loads(base64.urlsafe_b64decode(payload_b64))
# Flatten into an audit-friendly record
flat = {
"timestamp": record.get("timestamp"),
"subject": claims.get("sub"),
"issuer": claims.get("iss"),
"expired_at": datetime.fromtimestamp(
claims.get("exp", 0), tz=timezone.utc
).isoformat(),
}
outfile.write(json.dumps(flat) + "\n")
extract_claims_stream("token-audit.ndjson", "claims-extract.ndjson")multiprocessing.Pool pour répartir la validation sur plusieurs cœurs, chaque token étant indépendant.Erreurs courantes
Ces quatre erreurs reviennent régulièrement dans les revues de code et les questions Stack Overflow. Chacune est facile à commettre, et le message d'erreur que PyJWT vous donne ne pointe pas toujours directement vers la cause. J'ai vu le seul problème de nommage du paquet faire perdre des heures de débogage à quelqu'un qui installe la mauvaise bibliothèque et obtient une API complètement inattendue.
Problème : Exécuter pip install jwt ou pip install python-jwt installe un paquet entièrement différent. import jwt échoue ensuite ou vous donne une API que vous ne reconnaissez pas.
Correction : Installez toujours avec pip install PyJWT. L'import est import jwt. Vérifiez avec pip show PyJWT pour confirmer le bon paquet.
pip install jwt # or pip install python-jwt import jwt # wrong package — different API entirely
pip install PyJWT import jwt # correct — this is PyJWT print(jwt.__version__) # 2.x
Problème : Dans PyJWT 1.x, algorithms était optionnel et autorisait par défaut n'importe quel algorithme. Cela créait une vulnérabilité de sécurité où un attaquant pouvait définir alg: none. PyJWT 2.x lève désormais DecodeError si algorithms est absent.
Correction : Passez toujours algorithms sous forme de liste explicite. N'utilisez que les algorithmes avec lesquels votre application émet réellement des tokens.
# PyJWT 2.x — this raises DecodeError payload = jwt.decode(token, SECRET_KEY) # DecodeError: algorithms must be specified
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
Problème : Passer une chaîne secrète à jwt.decode() avec algorithms=["RS256"] lève InvalidSignatureError. RS256 nécessite une clé publique encodée en PEM, pas une chaîne secrète partagée.
Correction : Chargez la clé publique PEM depuis un fichier ou une variable d'environnement. Installez le paquet cryptography : pip install PyJWT cryptography.
# This fails — RS256 needs a public key, not a string secret payload = jwt.decode(token, "my-secret", algorithms=["RS256"]) # InvalidSignatureError
public_key = open("public_key.pem").read()
payload = jwt.decode(token, public_key, algorithms=["RS256"])Problème : L'encodage base64url des JWT supprime les caractères = de fin. base64.urlsafe_b64decode de Python lève binascii.Error: Incorrect padding si vous passez le segment brut sans corriger le padding.
Correction : Ajoutez le padding avant le décodage : segment += '=' * (-len(segment) % 4). Cette formule produit toujours le bon nombre de caractères de padding (0, 1, 2 ou 3).
import base64
payload_b64 = token.split(".")[1]
data = base64.urlsafe_b64decode(payload_b64)
# binascii.Error: Incorrect paddingimport base64
payload_b64 = token.split(".")[1]
payload_b64 += "=" * (-len(payload_b64) % 4)
data = base64.urlsafe_b64decode(payload_b64) # worksPyJWT vs alternatives — Comparaison rapide
PyJWT est le bon point de départ pour la plupart des applications Python. Il couvre HMAC et (avec le backend cryptography) la vérification de signatures RSA et EC. Si vous avez besoin de JWE (tokens chiffrés), passez à python-jose ou Authlib. Le décodage manuel en base64 fonctionne pour le débogage mais n'offre aucune garantie de sécurité.
Voici quand je choisis chaque option : PyJWT pour tout service web standard utilisant HS256 ou RS256. python-jose quand l'architecture inclut des tokens chiffrés ou une rotation JWKS. Le base64 manuel pour une inspection rapide dans des environnements où pip n'est pas disponible (conteneurs CI, hôtes de production restreints, démarrages à froid AWS Lambda où vous souhaitez minimiser les dépendances). Authlib quand le projet l'utilise déjà pour des flux client OAuth et qu'ajouter une autre bibliothèque JWT serait redondant.
Pour une alternative sans code, collez n'importe quel token dans le décodeur JWTpour voir l'en-tête et le payload décodés avec des retours sur la validation des claims.
Questions fréquentes
Comment décoder un JWT en Python sans vérifier la signature ?
Passez options={"verify_signature": False} et algorithms=["HS256"] à jwt.decode(). Cela retourne le dict du payload sans vérifier la signature. Utilisez cette approche uniquement pour l'inspection — lire les claims avant de récupérer la bonne clé publique, ou déboguer en développement. Ne sautez jamais la vérification sur des tokens qui contrôlent l'accès à quoi que ce soit.
import jwt
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
payload = jwt.decode(
token,
options={"verify_signature": False},
algorithms=["HS256"]
)
print(payload)
# {'sub': 'usr_8f2a', 'role': 'admin'}Quelle est la différence entre PyJWT et python-jwt ?
PyJWT (pip install PyJWT, import jwt) est la bibliothèque JWT la plus populaire pour Python, avec plus de 80 millions de téléchargements mensuels. python-jwt (pip install python_jwt) est une bibliothèque distincte, bien moins utilisée, avec une API différente. Si vous voyez import jwt dans le code de quelqu'un, il utilise PyJWT. La confusion vient du fait que le nom du paquet PyPI (PyJWT) diffère du nom d'import (jwt). Restez sur PyJWT sauf raison spécifique.
Comment décoder un JWT avec RS256 en Python ?
Installez PyJWT et le backend cryptography : pip install PyJWT cryptography. Passez ensuite la clé publique encodée en PEM comme argument key et algorithms=["RS256"]. PyJWT délègue la vérification de la signature RSA à la bibliothèque cryptography. Sans le paquet cryptography installé, PyJWT lève une erreur lorsque vous tentez d'utiliser des algorithmes RSA ou EC.
import jwt
public_key = open("public_key.pem").read()
payload = jwt.decode(
token,
public_key,
algorithms=["RS256"],
audience="https://api.example.com"
)Pourquoi PyJWT lève-t-il ExpiredSignatureError ?
PyJWT vérifie le claim exp (expiration) par défaut. Si l'heure UTC actuelle dépasse le timestamp exp, il lève jwt.ExpiredSignatureError. Vous pouvez ajouter une tolérance pour le décalage d'horloge avec le paramètre leeway : jwt.decode(token, key, algorithms=["HS256"], leeway=timedelta(seconds=30)). Cela accorde une période de grâce de 30 secondes. Pour désactiver entièrement la vérification d'expiration (non recommandé en production), passez options={"verify_exp": False}.
import jwt
from datetime import timedelta
try:
payload = jwt.decode(token, secret, algorithms=["HS256"], leeway=timedelta(seconds=30))
except jwt.ExpiredSignatureError:
print("Token has expired — prompt re-authentication")Peut-on lire les claims JWT sans aucune bibliothèque en Python ?
Oui. Découpez le token sur les points, prenez le deuxième segment (le payload), complétez-le avec des caractères = pour que la longueur soit un multiple de 4, puis décodez-le en base64url et parsez le JSON. Cela vous donne le dict des claims mais ne vérifie pas la signature. C'est utile dans des environnements restreints où vous ne pouvez pas installer PyJWT, ou pour des scripts de débogage rapide.
import base64, json
token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOGYyYSJ9.signature"
payload_b64 = token.split(".")[1]
payload_b64 += "=" * (-len(payload_b64) % 4) # fix padding
claims = json.loads(base64.urlsafe_b64decode(payload_b64))
print(claims) # {'sub': 'usr_8f2a'}Comment valider le claim audience avec PyJWT ?
Passez le paramètre audience à jwt.decode() : jwt.decode(token, key, algorithms=["HS256"], audience="https://api.example.com"). PyJWT compare le claim aud dans le token avec la valeur que vous fournissez. Si le token n'a pas de claim aud, ou si la valeur ne correspond pas, il lève jwt.InvalidAudienceError. Vous pouvez également passer une liste d'audiences acceptables si votre service accepte des tokens destinés à plusieurs APIs.
import jwt
payload = jwt.decode(
token,
secret,
algorithms=["HS256"],
audience=["https://api.example.com", "https://admin.example.com"]
)Outils associés
Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.
Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.