JWT Decoder Python — Decodificare JWT con PyJWT
Usa il Decoder JWT gratuito direttamente nel tuo browser — nessuna installazione.
Prova Decoder JWT online →Ogni API che utilizza l'autenticazione basata su token ti fornisce prima o poi un JWT, e capire cosa contiene è uno di quei compiti che si presenta costantemente durante lo sviluppo. Un decoder JWT in Python prende quella stringa base64 opaca e la trasforma in un dizionario di claim leggibile con cui puoi lavorare concretamente. Il pacchetto PyPI che cerchi è PyJWT — si installa con pip install PyJWT ma si importa come import jwt. Questa guida tratta jwt.decode() con verifica completa della firma, la decodifica senza segreto per ispezioni rapide, la decodifica manuale base64 senza alcuna libreria, la verifica con chiave pubblica RS256, e gli errori comuni che ho incontrato in sistemi di autenticazione in produzione. Per un controllo veloce una tantum, il JWT Decoder online lo fa istantaneamente senza scrivere codice. Tutti gli esempi sono per Python 3.10+ e PyJWT 2.x.
- ✓pip install PyJWT, poi import jwt — il nome del pacchetto e il nome di import sono diversi, il che mette in difficoltà quasi tutti.
- ✓jwt.decode(token, key, algorithms=["HS256"]) restituisce un semplice dict con i claim. Passa sempre algorithms in modo esplicito.
- ✓Per ispezionare i claim senza verifica: jwt.decode(token, options={"verify_signature": False}, algorithms=["HS256"]).
- ✓Per token RSA/EC: pip install PyJWT cryptography — il backend cryptography è necessario per gli algoritmi asimmetrici.
- ✓La decodifica manuale (base64 + json) funziona senza alcuna libreria ma salta tutta la validazione della firma e della scadenza.
Cos'è la Decodifica JWT?
Un JSON Web Token è composto da tre segmenti codificati in base64url separati da punti: un header (algoritmo e tipo di token), un payload (i claim — ID utente, ruoli, data di scadenza), e una firma. Decodificare un JWT significa estrarre i segmenti header e payload, decodificarli con base64url e analizzare il JSON risultante in un dizionario di claim.
L'header indica quale algoritmo è stato usato per firmare il token e a volte include un kid (key ID) per trovare la chiave di verifica corretta. Il payload contiene i dati effettivi: a chi è stato emesso il token (sub), quando scade (exp), per quale servizio è destinato (aud), più eventuali claim personalizzati che la tua applicazione definisce. Il segmento firma prova che il token non è stato manomesso, ma hai bisogno della chiave segreta o della chiave pubblica per verificarlo. Decodifica e verifica sono operazioni separate. Puoi decodificare il payload senza verificare la firma (utile per il debug), ma non fidarti mai di claim non verificati per le decisioni di autorizzazione.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxMTgxNTYwMH0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
{
"sub": "usr_8f2a",
"role": "admin",
"exp": 1711815600
}jwt.decode() — Decodifica e Verifica con PyJWT
jwt.decode() è la funzione principale della libreria PyJWT. Riceve la stringa del token codificato, la chiave segreta (per algoritmi HMAC) o la chiave pubblica (per RSA/EC), e un elenco algorithms obbligatorio. La funzione verifica la firma, controlla i claim standard come exp e nbf, e restituisce il payload come dizionario Python. Se qualcosa fallisce — firma errata, token scaduto, algoritmo sbagliato — solleva un'eccezione specifica.
Esempio Minimale Funzionante
import jwt
# Un segreto condiviso tra l'emittente e questo servizio
SECRET_KEY = "k8s-webhook-signing-secret-2026"
# Codifica prima un token (simulando ciò che emetterebbe un auth server)
token = jwt.encode(
{"sub": "usr_8f2a", "role": "admin", "team": "platform"},
SECRET_KEY,
algorithm="HS256"
)
print(token)
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3Jf...
# Decodifica e verifica il token
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
print(payload)
# {'sub': 'usr_8f2a', 'role': 'admin', 'team': 'platform'}
print(payload["role"])
# adminIl parametro algorithms è un elenco, non una stringa singola, ed è obbligatorio in PyJWT 2.x. Si tratta di una funzionalità di sicurezza: senza di esso, un attaccante potrebbe creare un token con alg: none nell'header e bypassare completamente la verifica. Specifica sempre esattamente quali algoritmi la tua applicazione accetta. Se emetti solo token HS256, l'elenco dovrebbe essere ["HS256"] — non ["HS256", "RS256", "none"]. Tenere l'elenco ristretto riduce la superficie di attacco.
Una cosa che mi ha confuso all'inizio: PyJWT 2.x ha modificato jwt.encode() per restituire una stringa anziché bytes. Se leggi vecchie risposte su Stack Overflow che chiamano .decode("utf-8") sul token codificato, quel codice è dell'era PyJWT 1.x e genererà un AttributeError nella versione 2.x. Il token è già una stringa — usalo direttamente.
Ciclo Completo con Scadenza
import jwt
from datetime import datetime, timedelta, timezone
SECRET_KEY = "webhook-processor-secret"
# Crea un token che scade in 1 ora
payload = {
"sub": "svc_payment_processor",
"iss": "auth.interno.esempio.com",
"aud": "https://api.esempio.com",
"exp": datetime.now(timezone.utc) + timedelta(hours=1),
"permissions": ["orders:read", "refunds:create"],
}
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
# In seguito, quando il token arriva nell'header della richiesta:
try:
decoded = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"],
audience="https://api.esempio.com",
issuer="auth.interno.esempio.com",
)
print(f"Servizio: {decoded['sub']}")
print(f"Permessi: {decoded['permissions']}")
except jwt.ExpiredSignatureError:
print("Token scaduto — richiedi una nuova autenticazione")
except jwt.InvalidAudienceError:
print("Token non destinato a questa API")
except jwt.InvalidIssuerError:
print("Token emesso da un'autorità sconosciuta")datetime in timestamp Unix automaticamente durante la codifica. Durante la decodifica, i claim exp, iat e nbf vengono restituiti come interi, non come oggetti datetime. Devi convertirli tu stesso con datetime.fromtimestamp(payload["exp"], tz=timezone.utc).Decodifica JWT Senza Verifica della Firma
A volte è necessario leggere i claim prima di poter verificare il token. Uno scenario comune: l'header del token contiene un kid (key ID) e devi recuperare la chiave pubblica corrispondente da un endpoint JWKS prima di poter verificare. PyJWT supporta questo con l'opzione verify_signature: False. Passi comunque l'elenco algorithms, ma l'argomento key viene ignorato.
import jwt
token = (
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNpZy0xNzI2In0"
".eyJzdWIiOiJ1c3JfM2M3ZiIsInNjb3BlIjoicmVhZDpvcmRlcnMiLCJpc3MiOiJhdXRoLmV4YW1wbGUuY29tIn0"
".signature_placeholder"
)
# Passo 1: leggi i claim senza verifica per ottenere info di routing
unverified = jwt.decode(
token,
options={"verify_signature": False},
algorithms=["RS256"]
)
print(unverified)
# {'sub': 'usr_3c7f', 'scope': 'read:orders', 'iss': 'auth.example.com'}
# Passo 2: leggi l'header per sapere quale chiave usare
header = jwt.get_unverified_header(token)
print(header)
# {'alg': 'RS256', 'typ': 'JWT', 'kid': 'sig-1726'}
# Ora usa header['kid'] per recuperare la chiave pubblica corretta dal tuo endpoint JWKSC'è una distinzione sottile. jwt.get_unverified_header() legge solo l'header — il primo segmento. La chiamata jwt.decode() con verify_signature: False legge il payload (secondo segmento). Tra i due puoi estrarre tutto da un token senza una chiave. PyJWT valida comunque che il token abbia la struttura corretta (tre segmenti separati da punti, base64 valido, JSON valido) anche quando la verifica della firma è disattivata. Se il token è strutturalmente malformato, solleva DecodeError indipendentemente dalle opzioni che passi.
Riferimento Parametri jwt.decode()
La firma completa è jwt.decode(jwt, key, algorithms, options, audience, issuer, leeway, require). Tutti i parametri dopo algorithms sono solo keyword.
Il dict options offre un controllo granulare su quali validazioni esegue PyJWT. Le chiavi corrispondono a controlli individuali: verify_signature, verify_exp, verify_nbf, verify_iss, verify_aud, e verify_iat. Tutti hanno default True a meno che non vengano impostati esplicitamente a False. In produzione, lascia tutti questi valori ai loro default. Disabilito singoli controlli solo durante lo sviluppo quando lavoro con token di test obsoleti e ho bisogno di bypassare temporaneamente la scadenza.
import jwt
# Richiede che siano presenti claim specifici — solleva MissingRequiredClaimError se assenti
payload = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"],
options={"require": ["exp", "iss", "sub"]},
issuer="auth.interno.esempio.com",
)
# Solo in sviluppo: salta la scadenza per testare con token vecchi
dev_payload = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"],
options={"verify_exp": False}, # NON usare in produzione
)Decodifica JWT Manuale con base64 e json
Puoi decodificare un payload JWT usando solo la libreria standard Python — senza pip install necessario. Questo è genuinamente utile in diverse situazioni: script di debug dove aggiungere una dipendenza è eccessivo, ambienti CI con restrizioni dove è disponibile solo la libreria standard, funzioni AWS Lambda dove vuoi minimizzare il tempo di avvio a freddo, o semplicemente per capire cosa sia effettivamente un JWT. Il processo è semplice: dividi sui punti, prendi il segmento che vuoi, aggiungi il padding base64, decodifica e analizza il JSON.
I moduli base64 e json fanno entrambi parte della libreria standard Python, quindi questo approccio funziona su qualsiasi installazione Python dalla 3.6 in poi. Le funzioni seguenti gestiscono separatamente l'header (primo segmento) e il payload (secondo segmento):
import base64
import json
def decode_jwt_payload(token: str) -> dict:
"""Decodifica il payload JWT senza verifica della firma.
Funziona con qualsiasi JWT — HS256, RS256, ES256, ecc.
"""
parts = token.split(".")
if len(parts) != 3:
raise ValueError(f"Expected 3 JWT segments, got {len(parts)}")
payload_b64 = parts[1]
# base64url usa - e _ invece di + e /
# urlsafe_b64decode di Python gestisce questo, ma richiede il 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:
"""Decodifica l'header JWT (algoritmo, key ID, tipo)."""
header_b64 = token.split(".")[0]
header_b64 += "=" * (-len(header_b64) % 4)
return json.loads(base64.urlsafe_b64decode(header_b64))
# Esempio d'uso
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: adminIl trucco del padding (+= "=" * (-len(s) % 4)) è la parte che tutti dimenticano. Il base64url di JWT omette i caratteri = finali, ma urlsafe_b64decode di Python li richiede. Senza la correzione del padding, ottieni un binascii.Error: Incorrect padding.
jwt.decode() con una chiave reale.Decodifica JWT da Risposte API e File Token
I due scenari reali più comuni: estrarre un JWT da una risposta HTTP (un endpoint token OAuth, un'API di login), e leggere token da file (credenziali di account di servizio, segreti montati Kubernetes, token in cache su disco). Entrambi richiedono una gestione adeguata degli errori. Le richieste di rete falliscono. I file scompaiono. I token scadono tra il momento in cui vengono messi in cache e il momento in cui vengono letti.
Gli esempi seguenti usano httpx per le chiamate HTTP (sostituisci con requests se preferisci, il pattern è identico) e pathlib.Path per le operazioni sui file. Ogni esempio intercetta eccezioni PyJWT specifiche anziché un generico except Exception, così puoi rispondere appropriatamente a ogni modalità di errore: ri-autenticazione alla scadenza, allerta per errori di firma, retry in caso di timeout di rete.
Decodifica JWT da una Risposta API
import jwt
import httpx # or requests
TOKEN_ENDPOINT = "https://auth.esempio.com/oauth/token"
SECRET_KEY = "shared-webhook-signing-key"
def get_and_decode_token() -> dict:
"""Recupera un access token dall'auth server e lo decodifica."""
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.esempio.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')}")Decodifica JWT da un File
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:
"""Legge un JWT da un file e lo verifica con una chiave pubblica PEM."""
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.esempio.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']}")Decodifica JWT da Riga di Comando
A volte hai solo bisogno di ispezionare un token dal terminale senza scrivere uno script. Magari stai facendo debug di un flusso OAuth e vuoi vedere cosa c'è nell'header Authorization, oppure hai recuperato un token dai DevTools del browser e vuoi controllarne la scadenza. Il flag -c di Python rende tutto questo un one-liner. Pipe il token e ottieni i claim come JSON formattato. Nessun file di script, nessun ambiente virtuale.
# Decodifica payload JWT dagli appunti o da una variabile (senza verifica)
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"
# }# Decodifica l'header JWT per verificare algoritmo e 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"
# }# Se PyJWT è installato, verifica e decodifica in un solo passaggio
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"Per un'alternativa visiva senza nessuna configurazione del terminale, incolla il token nel JWT Decoder di ToolDeck e vedi istantaneamente header, payload e stato di verifica della firma.
python-jose e Altre Alternative
python-jose è una libreria JWT alternativa che supporta nativamente JWS, JWE (token cifrati) e JWK. Se la tua applicazione deve gestire JWT cifrati (JWE) — dove il payload stesso è cifrato, non solo firmato — python-jose è la scelta giusta perché PyJWT non supporta JWE affatto. La libreria ha anche la gestione integrata di set di chiavi JWKS, che semplifica l'integrazione con provider di identità come Auth0, Okta o Keycloak che espongono set di chiavi rotanti. L'interfaccia di decodifica è quasi identica a PyJWT, quindi passare da una all'altra richiede modifiche minime al codice:
# pip install python-jose[cryptography]
from jose import jwt as jose_jwt
token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInNjb3BlIjoib3JkZXJzOnJlYWQifQ.signature"
# Decodifica verificata — stesso pattern di PyJWT
payload = jose_jwt.decode(
token,
"signing-secret-key",
algorithms=["HS256"],
audience="https://api.esempio.com",
)
print(payload)
# {'sub': 'usr_8f2a', 'scope': 'orders:read'}
# Decodifica non verificata
claims = jose_jwt.get_unverified_claims(token)
header = jose_jwt.get_unverified_header(token)
print(f"Algorithm: {header['alg']}, Subject: {claims['sub']}")La mia raccomandazione: inizia con PyJWT. Copre il 95% dei casi d'uso JWT, ha la comunità più ampia e l'API è pulita. Passa a python-jose se hai bisogno del supporto JWE o preferisci la sua gestione JWKS. Una terza opzione degna di nota è Authlib, che include la gestione JWT all'interno di un framework OAuth/OIDC molto più ampio. Se stai già usando Authlib per i flussi client OAuth, il suo modulo authlib.jose.jwt ti evita di aggiungere una seconda dipendenza JWT. Altrimenti, è una dipendenza pesante solo per la decodifica dei token.
Output nel Terminale con Syntax Highlighting
Leggere i claim JWT grezzi in un terminale va bene per controlli rapidi, ma quando fai debug dei payload dei token regolarmente (io lo facevo quotidianamente mentre sviluppavo un gateway di autenticazione interno a Milano), l'output colorato fa davvero la differenza. Valori stringa, numeri, booleani e null vengono visualizzati in colori distinti, il che significa che puoi individuare un permesso mancante o un timestamp di scadenza errato a colpo d'occhio senza leggere ogni singolo carattere.
La libreria rich (pip install rich) ha una funzione print_json che accetta una stringa JSON o un dict Python e lo stampa con syntax highlighting completo nel terminale. Combinala con PyJWT per un flusso di ispezione JWT in due righe:
# 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"]
)
# Output JSON colorato e indentato nel terminale
print_json(data=payload)
# {
# "sub": "usr_8f2a", ← stringhe in verde
# "role": "admin",
# "permissions": [
# "orders:read",
# "refunds:create"
# ],
# "exp": 1711815600 ← numeri in ciano
# }rich contiene codici di escape ANSI. Non scriverlo su file o restituirlo da endpoint API — è solo per la visualizzazione nel terminale. Usa json.dumps() quando hai bisogno di output in testo semplice.Elaborazione di Grandi Batch di Token
I token JWT sono piccoli (tipicamente sotto i 2 KB ciascuno), ma esistono scenari in cui li elabori in blocco. Analisi dei log di audit dopo un incidente di sicurezza. Script di migrazione delle sessioni quando si cambia provider di autenticazione. Validazione batch per conformità normativa dove devi dimostrare che ogni token emesso negli ultimi 90 giorni è stato firmato con la chiave corretta. Se hai decine di migliaia di token in un file di log NDJSON, elaborarli riga per riga evita di caricare l'intero file in memoria e ti permette di riportare i risultati in modo incrementale.
Validazione Batch di Token da un Log di Audit
import jwt
import json
from pathlib import Path
SECRET_KEY = "audit-log-signing-key"
def validate_token_log(log_path: str) -> dict:
"""Elabora un file NDJSON dove ogni riga ha un campo 'token'.
Restituisce i conteggi di token validi, scaduti e non validi.
"""
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: 17Estrazione dei Claim da un Export NDJSON di Token
import base64
import json
from datetime import datetime, timezone
def extract_claims_stream(input_path: str, output_path: str):
"""Legge i token riga per riga, decodifica i payload, scrive i claim appiattiti."""
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))
# Appiattisci in un record adatto all'audit
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 per distribuire la validazione su più core, poiché ogni token è indipendente.Errori Comuni
Questi quattro errori emergono ripetutamente nelle revisioni del codice e nelle domande su Stack Overflow. Ognuno è facile da commettere e il messaggio di errore che PyJWT fornisce non punta sempre direttamente alla causa. Ho visto solo il problema di naming dei pacchetti sprecare ore di debug quando qualcuno installa la libreria sbagliata e ottiene un'API completamente inaspettata.
Problema: Eseguire pip install jwt o pip install python-jwt installa un pacchetto completamente diverso. import jwt poi fallisce o ti dà un'API che non riconosci.
Soluzione: Installa sempre con pip install PyJWT. L'import è import jwt. Verifica con pip show PyJWT che sia installato il pacchetto corretto.
pip install jwt # or pip install python-jwt import jwt # pacchetto sbagliato — API completamente diversa
pip install PyJWT import jwt # corretto — questo è PyJWT print(jwt.__version__) # 2.x
Problema: In PyJWT 1.x, algorithms era opzionale e consentiva qualsiasi algoritmo per impostazione predefinita. Questo creava una vulnerabilità di sicurezza in cui un attaccante poteva impostare alg: none. PyJWT 2.x ora solleva DecodeError se algorithms è assente.
Soluzione: Passa sempre algorithms come elenco esplicito. Usa solo gli algoritmi con cui la tua applicazione emette effettivamente i token.
# PyJWT 2.x — questo solleva DecodeError payload = jwt.decode(token, SECRET_KEY) # DecodeError: algorithms must be specified
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
Problema: Passare un segreto stringa a jwt.decode() con algorithms=["RS256"] solleva InvalidSignatureError. RS256 richiede una chiave pubblica in formato PEM, non una stringa segreta condivisa.
Soluzione: Carica la chiave pubblica PEM da un file o una variabile d'ambiente. Installa il pacchetto cryptography: pip install PyJWT cryptography.
# Questo fallisce — RS256 richiede una chiave pubblica, non un segreto stringa payload = jwt.decode(token, "my-secret", algorithms=["RS256"]) # InvalidSignatureError
public_key = open("public_key.pem").read()
payload = jwt.decode(token, public_key, algorithms=["RS256"])Problema: La codifica base64url di JWT elimina i caratteri = finali. base64.urlsafe_b64decode di Python solleva binascii.Error: Incorrect padding se passi il segmento grezzo senza correggere il padding.
Soluzione: Aggiungi il padding prima di decodificare: segment += '=' * (-len(segment) % 4). Questa formula produce sempre il numero corretto di caratteri di padding (0, 1, 2 o 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) # funzionaPyJWT vs Alternative — Confronto Rapido
PyJWT è il punto di partenza giusto per la maggior parte delle applicazioni Python. Copre HMAC e (con il backend cryptography) la verifica delle firme RSA ed EC. Se hai bisogno di JWE (token cifrati), passa a python-jose o Authlib. La decodifica manuale base64 funziona per il debug ma non offre alcuna garanzia di sicurezza.
Ecco quando scelgo ciascuna opzione: PyJWT per qualsiasi servizio web standard che fa verifica HS256 o RS256. python-jose quando l'architettura include token cifrati o rotazione JWKS. Base64 manuale per ispezioni rapide in ambienti dove pip non è disponibile (container CI, host di produzione con restrizioni, avvii a freddo AWS Lambda dove si vuole minimizzare le dipendenze). Authlib quando il progetto lo usa già per i flussi client OAuth e aggiungere un'altra libreria JWT sarebbe ridondante.
Per un'alternativa senza codice, incolla qualsiasi token nel JWT Decoder per vedere l'header e il payload decodificati con feedback di validazione dei claim.
Domande Frequenti
Come decodifico un JWT in Python senza verificare la firma?
Passa options={"verify_signature": False} e algorithms=["HS256"] a jwt.decode(). Questo restituisce il dict del payload senza controllare la firma. Usalo solo per ispezione — leggere i claim prima di recuperare la chiave pubblica corretta, o per debug in sviluppo. Non saltare mai la verifica su token che proteggono l'accesso a risorse reali.
import jwt
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
payload = jwt.decode(
token,
options={"verify_signature": False},
algorithms=["HS256"]
)
print(payload)
# {'sub': 'usr_8f2a', 'role': 'admin'}Qual è la differenza tra PyJWT e python-jwt?
PyJWT (pip install PyJWT, import jwt) è la libreria JWT più diffusa per Python, con oltre 80 milioni di download mensili. python-jwt (pip install python_jwt) è una libreria separata, molto meno usata, con una superficie API diversa. Se vedi import jwt nel codice di qualcuno, sta usando PyJWT. La confusione nasce dal nome del pacchetto PyPI (PyJWT) che differisce dal nome di import (jwt). Usa PyJWT a meno che tu non abbia un motivo specifico per fare altrimenti.
Come decodifico un JWT con RS256 in Python?
Installa sia PyJWT che il backend cryptography: pip install PyJWT cryptography. Poi passa la chiave pubblica in formato PEM come argomento key e algorithms=["RS256"]. PyJWT delega la verifica della firma RSA alla libreria cryptography. Senza il pacchetto cryptography installato, PyJWT solleva un errore quando si tenta di usare algoritmi RSA o EC.
import jwt
public_key = open("public_key.pem").read()
payload = jwt.decode(
token,
public_key,
algorithms=["RS256"],
audience="https://api.example.com"
)Perché PyJWT solleva ExpiredSignatureError?
PyJWT controlla il claim exp (scadenza) per impostazione predefinita. Se l'ora UTC corrente è successiva al timestamp exp, solleva jwt.ExpiredSignatureError. Puoi aggiungere tolleranza per desfasamento orario con il parametro leeway: jwt.decode(token, key, algorithms=["HS256"], leeway=timedelta(seconds=30)). Questo concede un margine di 30 secondi. Per disabilitare completamente il controllo di scadenza (sconsigliato in produzione), passa 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 scaduto — richiedi una nuova autenticazione")Posso leggere i claim JWT senza alcuna libreria in Python?
Sì. Dividi il token sui punti, prendi il secondo segmento (il payload), aggiungici caratteri = per rendere la lunghezza un multiplo di 4, poi decodificalo con base64url e analizza il JSON risultante. Questo ti restituisce il dict dei claim ma non verifica la firma. È utile in ambienti con restrizioni dove non puoi installare PyJWT, o per script di debug rapidi.
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'}Come valido il claim audience con PyJWT?
Passa il parametro audience a jwt.decode(): jwt.decode(token, key, algorithms=["HS256"], audience="https://api.example.com"). PyJWT confronta il claim aud nel token con il valore che fornisci. Se il token non ha un claim aud, o il valore non corrisponde, solleva jwt.InvalidAudienceError. Puoi anche passare un elenco di audience accettabili se il tuo servizio accetta token destinati a più API.
import jwt
payload = jwt.decode(
token,
secret,
algorithms=["HS256"],
audience=["https://api.example.com", "https://admin.example.com"]
)Strumenti Correlati
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.