JWT Decoder Python — JWTs mit PyJWT dekodieren
Nutze das kostenlose JWT Decoder direkt im Browser – keine Installation erforderlich.
JWT Decoder online testen →Jede API, die Token-basierte Authentifizierung verwendet, liefert dir irgendwann einen JWT, und herauszufinden, was darin steckt, ist eine Aufgabe, die bei der Entwicklung ständig auftaucht. Ein JWT-Decoder in Python nimmt diesen opaken Base64-String und wandelt ihn in ein lesbares Claims-Dictionary um, mit dem du tatsächlich arbeiten kannst. Das PyPI-Paket, das du brauchst, ist PyJWT — installiert mit pip install PyJWT aber importiert als import jwt. Dieser Leitfaden behandelt jwt.decode() mit vollständiger Signaturverifikation, Dekodierung ohne Geheimnis für schnelle Inspektion, manuelles Base64-Dekodieren ohne Bibliothek, RS256-Verifikation mit öffentlichem Schlüssel und häufige Fehler aus echten Produktionssystemen. Für eine schnelle Einmalprüfung erledigt der Online-JWT-Decoder das sofort ohne Code. Alle Beispiele richten sich an Python 3.10+ und PyJWT 2.x.
- ✓pip install PyJWT, dann import jwt — Paketname und Import-Name sind unterschiedlich, was fast jeden stolpert.
- ✓jwt.decode(token, key, algorithms=["HS256"]) gibt ein einfaches Dict mit den Claims zurück. algorithms immer explizit angeben.
- ✓Claims ohne Verifikation inspizieren: jwt.decode(token, options={"verify_signature": False}, algorithms=["HS256"]).
- ✓Für RSA/EC-Tokens: pip install PyJWT cryptography — das cryptography-Backend ist für asymmetrische Algorithmen erforderlich.
- ✓Manuelles Dekodieren (base64 + json) funktioniert ohne Bibliothek, überspringt aber alle Signatur- und Ablaufprüfungen.
Was ist JWT-Dekodierung?
Ein JSON Web Token besteht aus drei base64url-kodierten Segmenten, getrennt durch Punkte: einem Header (Algorithmus und Token-Typ), einem Payload (die Claims — Benutzer-ID, Rollen, Ablaufzeit) und einer Signatur. Einen JWT zu dekodieren bedeutet, die Header- und Payload-Segmente zu extrahieren, sie base64url zu dekodieren und das resultierende JSON in ein Dictionary von Claims zu parsen.
Der Header verrät dir, welcher Algorithmus zum Signieren des Tokens verwendet wurde, und enthält manchmal eine kid (Key-ID) zum Auffinden des richtigen Verifikationsschlüssels. Der Payload enthält die eigentlichen Daten: für wen der Token ausgestellt wurde (sub), wann er abläuft (exp), für welchen Dienst er bestimmt ist (aud), sowie benutzerdefinierte Claims deiner Anwendung. Das Signatur-Segment beweist, dass der Token nicht manipuliert wurde, aber du brauchst den geheimen Schlüssel oder den öffentlichen Schlüssel, um es zu verifizieren. Dekodieren und Verifikation sind separate Operationen. Du kannst den Payload dekodieren, ohne die Signatur zu verifizieren (nützlich zum Debuggen), aber vertraue nie unverifizierte Claims für Autorisierungsentscheidungen.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxMTgxNTYwMH0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
{
"sub": "usr_8f2a",
"role": "admin",
"exp": 1711815600
}jwt.decode() — Dekodieren und Verifizieren mit PyJWT
jwt.decode() ist die primäre Funktion der PyJWT-Bibliothek. Sie nimmt den kodierten Token-String, den geheimen Schlüssel (für HMAC-Algorithmen) oder den öffentlichen Schlüssel (für RSA/EC) und eine verpflichtende algorithms-Liste. Die Funktion verifiziert die Signatur, prüft Standard-Claims wie exp und nbfund gibt den Payload als Python-Dictionary zurück. Schlägt irgendetwas fehl — ungültige Signatur, abgelaufener Token, falscher Algorithmus — wirft sie eine spezifische Ausnahme.
Minimales Arbeitsbeispiel
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"])
# adminDer algorithms Parameter ist eine Liste, kein einzelner String, und in PyJWT 2.x verpflichtend. Das ist eine Sicherheitsfunktion: Ohne sie könnte ein Angreifer einen Token mit alg: none im Header erstellen und die Verifikation vollständig umgehen. Gib immer genau an, welche Algorithmen deine Anwendung akzeptiert. Wenn du nur HS256-Tokens ausstellst, sollte die Liste nur ["HS256"] sein — nicht ["HS256", "RS256", "none"]. Eine enge Liste verringert die Angriffsfläche.
Etwas, das mich anfangs verwirrt hat: PyJWT 2.x hat jwt.encode() so geändert, dass es einen String statt Bytes zurückgibt. Wenn du alte Stack-Overflow-Antworten liest, die .decode("utf-8") auf den kodierten Token aufrufen, stammt dieser Code aus der PyJWT-1.x-Ära und wirft einen AttributeError in Version 2.x. Der Token ist bereits ein String — verwende ihn direkt.
Vollständiger Durchlauf mit Ablaufzeit
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.intern.beispiel.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.intern.beispiel.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")datetime-Objekte beim Kodieren automatisch in Unix-Zeitstempel um. Beim Dekodieren kommen die exp-, iat- und nbf-Claims als Integer zurück, nicht als datetime-Objekte. Du musst sie selbst mit datetime.fromtimestamp(payload["exp"], tz=timezone.utc) konvertieren.JWT ohne Signaturverifikation dekodieren
Manchmal musst du die Claims lesen, bevor du den Token verifizieren kannst. Ein häufiges Szenario: Der Token-Header enthält ein kid (Key-ID)-Feld, und du musst den passenden öffentlichen Schlüssel von einem JWKS-Endpunkt abrufen, bevor du verifizieren kannst. PyJWT unterstützt das mit der verify_signature: False Option. Du übergibst weiterhin die algorithms-Liste, aber das key-Argument wird ignoriert.
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 endpointHier gibt es einen feinen Unterschied. jwt.get_unverified_header() liest nur den Header — das erste Segment. Der jwt.decode()-Aufruf mit verify_signature: False liest den Payload (zweites Segment). Zusammen kannst du alles aus einem Token extrahieren, ohne einen Schlüssel zu haben. PyJWT prüft trotzdem, dass der Token die richtige Struktur hat (drei durch Punkte getrennte Segmente, gültiges Base64, gültiges JSON), auch wenn die Signaturverifikation deaktiviert ist. Ist der Token strukturell fehlerhaft, wirft es DecodeError unabhängig von den übergebenen Optionen.
jwt.decode() Parameter-Referenz
Die vollständige Signatur lautet jwt.decode(jwt, key, algorithms, options, audience, issuer, leeway, require). Alle Parameter nach algorithms sind nur als Keyword-Argumente zulässig.
Das options-Dict gibt feingranulare Kontrolle darüber, welche Validierungen PyJWT durchführt. Die Schlüssel entsprechen einzelnen Prüfungen: verify_signature, verify_exp, verify_nbf, verify_iss, verify_audund verify_iat. Alle sind standardmäßig True, außer wenn du sie explizit auf False setzt. In der Produktion lass alle auf ihren Standardwerten. Einzelne Prüfungen deaktiviere ich nur in der Entwicklung, wenn ich mit veralteten Test-Tokens arbeite und die Ablaufprüfung vorübergehend umgehen muss.
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.intern.beispiel.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
)Manuelles JWT-Dekodieren mit base64 und json
Du kannst einen JWT-Payload mit nichts weiter als der Python-Standardbibliothek dekodieren — kein pip install nötig. Das ist in mehreren Situationen wirklich nützlich: Debugging-Skripte, bei denen eine Abhängigkeit überdimensioniert wäre, eingeschränkte CI-Umgebungen, in denen nur die Standardbibliothek verfügbar ist, AWS-Lambda-Funktionen, bei denen du die Kaltstartzeit minimieren möchtest, oder einfach um zu verstehen, was ein JWT unter der Haube ist. Der Ablauf ist unkompliziert: auf Punkte aufteilen, das gewünschte Segment nehmen, Base64-Padding hinzufügen, dekodieren und das JSON parsen.
Die base64- und json-Module sind beide in der Python-Standardbibliothek enthalten, daher funktioniert dieser Ansatz mit jeder Python-Installation ab 3.6. Die folgenden Funktionen verarbeiten den Header (erstes Segment) und den Payload (zweites Segment) separat:
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: adminDer Padding-Trick (+= "=" * (-len(s) % 4)) ist der Teil, den alle vergessen. JWT-Base64url lässt abschließende =-Zeichen weg, aber Pythons urlsafe_b64decode benötigt sie. Ohne den Padding-Fix erhältst du einen binascii.Error: Incorrect padding.
jwt.decode() mit einem echten Schlüssel.JWTs aus API-Antworten und Token-Dateien dekodieren
Die zwei häufigsten Praxisszenarien: einen JWT aus einer HTTP-Antwort extrahieren (ein OAuth-Token-Endpunkt, eine Login-API) und Tokens aus Dateien lesen (Service-Account-Anmeldedaten, in Kubernetes eingebundene Secrets, zwischengespeicherte Tokens auf der Festplatte). Beide benötigen ordentliches Fehlerhandling. Netzwerkanfragen schlagen fehl. Dateien fehlen. Tokens laufen zwischen dem Zeitpunkt der Zwischenspeicherung und dem Lesen ab.
Die folgenden Beispiele verwenden httpx für HTTP-Aufrufe (tausch es gegen requests aus, wenn du es bevorzugst — das Muster ist identisch) und pathlib.Path für Dateioperationen. Jedes Beispiel fängt spezifische PyJWT-Ausnahmen ab statt eines allgemeinen except Exception, damit du auf jeden Fehlerfall angemessen reagieren kannst: bei Ablauf neu authentifizieren, bei Signaturfehler alarmieren, bei Netzwerk-Timeout erneut versuchen.
JWT aus einer API-Antwort dekodieren
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')}")JWT aus einer Datei dekodieren
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']}")JWT-Dekodierung über die Kommandozeile
Manchmal möchtest du einen Token einfach im Terminal inspizieren, ohne ein Skript zu schreiben. Vielleicht debuggst du einen OAuth-Flow und möchtest sehen, was im Authorization-Header steckt, oder du hast einen Token aus den Browser-DevTools kopiert und möchtest seine Ablaufzeit prüfen. Pythons -c-Flag macht das zu einem Einzeiler. Den Token per Pipe übergeben und die Claims als formatiertes JSON erhalten. Kein Skript, keine virtuelle Umgebung.
# 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"Als visuelle Alternative ohne Terminal-Setup kannst du deinen Token in den ToolDeck JWT Decoder einfügen und Header, Payload und Signatur-Verifikationsstatus sofort einsehen.
python-jose und andere Alternativen
python-jose ist eine alternative JWT-Bibliothek, die JWS, JWE (verschlüsselte Tokens) und JWK nativ unterstützt. Wenn deine Anwendung verschlüsselte JWTs (JWE) verarbeiten muss — bei denen der Payload selbst verschlüsselt ist, nicht nur signiert — ist python-jose die richtige Wahl, da PyJWT JWE nicht unterstützt. Die Bibliothek hat auch eine integrierte JWKS-Key-Set-Verwaltung, was die Integration mit Identity-Providern wie Auth0, Okta oder Keycloak vereinfacht, die rotierende Schlüsselsätze bereitstellen. Die Decode-Schnittstelle ist fast identisch mit PyJWT, sodass ein Wechsel minimale Codeänderungen erfordert:
# 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']}")Meine Empfehlung: Fang mit PyJWT an. Es deckt 95 % der JWT-Anwendungsfälle ab, hat die größte Community und eine saubere API. Wechsle zu python-jose, wenn du JWE-Unterstützung benötigst oder seine JWKS-Verwaltung bevorzugst. Eine dritte erwähnenswerte Option ist Authlib, das JWT-Handling in ein wesentlich größeres OAuth/OIDC-Framework einbettet. Wenn du Authlib bereits für OAuth-Client-Flows verwendest, erspart dir das authlib.jose.jwt-Modul eine zweite JWT-Abhängigkeit. Andernfalls ist es eine schwergewichtige Abhängigkeit nur für das Token-Dekodieren.
Terminal-Ausgabe mit Syntaxhervorhebung
Rohe JWT-Claims im Terminal zu lesen ist für schnelle Prüfungen in Ordnung, aber wenn du Token-Payloads regelmäßig debuggst (ich habe das täglich beim Aufbau eines internen Auth-Gateways gemacht), macht farbige Ausgabe einen echten Unterschied. Strings, Zahlen, Booleans und null werden in unterschiedlichen Farben dargestellt, sodass du eine fehlende Berechtigung oder einen falschen Ablauf-Zeitstempel auf einen Blick erkennst, ohne jeden Buchstaben zu lesen.
Die rich-Bibliothek (pip install rich) hat eine print_json-Funktion, die entweder einen JSON-String oder ein Python-Dict nimmt und es mit vollständiger Syntaxhervorhebung im Terminal ausgibt. Kombiniere sie mit PyJWT für einen Zwei-Zeilen-JWT-Inspektions-Workflow:
# 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
# }rich-Ausgabe enthält ANSI-Escape-Codes. Schreibe sie nicht in Dateien oder gib sie von API-Endpunkten zurück — sie ist nur für die Terminal-Anzeige gedacht. Verwende json.dumps(), wenn du einfache Textausgabe benötigst.Mit großen Token-Mengen arbeiten
JWT-Tokens selbst sind klein (typischerweise unter 2 KB pro Stück), aber es gibt Szenarien, in denen du sie im Batch verarbeitest. Audit-Log-Analyse nach einem Sicherheitsvorfall. Session-Migrations-Skripte beim Wechsel des Auth-Providers. Compliance-Batch-Validierung, bei der du nachweisen musst, dass jeder in den letzten 90 Tagen ausgestellte Token mit dem richtigen Schlüssel signiert wurde. Wenn du zehntausende von Tokens in einer NDJSON-Log-Datei hast, vermeidest du durch zeilenweise Verarbeitung, die gesamte Datei in den Speicher zu laden, und kannst Ergebnisse schrittweise ausgeben.
Tokens aus einem Audit-Log im Batch validieren
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: 17Claims aus NDJSON-Token-Export extrahieren
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, um die Validierung auf mehrere Kerne zu verteilen, da jeder Token unabhängig ist.Häufige Fehler
Diese vier Fehler tauchen immer wieder in Code-Reviews und Stack-Overflow-Fragen auf. Jeder ist leicht zu machen, und die Fehlermeldung, die PyJWT ausgibt, zeigt nicht immer direkt auf die Ursache. Das Paket-Benennungsproblem allein hat schon stundenlange Debugging-Zeit verschwendet, als jemand die falsche Bibliothek installiert hat und eine völlig unerwartete API bekommen hat.
Problem: pip install jwt oder pip install python-jwt installiert ein völlig anderes Paket. import jwt schlägt dann fehl oder gibt eine unbekannte API zurück.
Lösung: Immer mit pip install PyJWT installieren. Der Import ist import jwt. Mit pip show PyJWT prüfen, ob das richtige Paket installiert ist.
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
Problem: In PyJWT 1.x war algorithms optional und ließ standardmäßig jeden Algorithmus zu. Das schuf eine Sicherheitslücke, bei der ein Angreifer alg: none setzen konnte. PyJWT 2.x wirft jetzt DecodeError, wenn algorithms fehlt.
Lösung: algorithms immer als explizite Liste übergeben. Nur die Algorithmen verwenden, die die Anwendung tatsächlich für Tokens nutzt.
# 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"])
Problem: Einen String-Geheimnis an jwt.decode() mit algorithms=["RS256"] übergeben wirft InvalidSignatureError. RS256 benötigt einen PEM-kodierten öffentlichen Schlüssel, keinen gemeinsamen Geheimnis-String.
Lösung: Den PEM-öffentlichen Schlüssel aus einer Datei oder Umgebungsvariable laden. Das cryptography-Paket installieren: 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"])Problem: JWT-Base64url-Kodierung lässt abschließende =-Zeichen weg. Pythons base64.urlsafe_b64decode wirft binascii.Error: Incorrect padding, wenn du das rohe Segment ohne Padding-Korrektur übergibst.
Lösung: Padding vor dem Dekodieren hinzufügen: segment += '=' * (-len(segment) % 4). Diese Formel erzeugt immer die korrekte Anzahl von Padding-Zeichen (0, 1, 2 oder 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. Alternativen — Kurzvergleich
PyJWT ist der richtige Ausgangspunkt für die meisten Python-Anwendungen. Es deckt HMAC und (mit dem cryptography-Backend) RSA- und EC-Signaturverifikation ab. Wenn du JWE (verschlüsselte Tokens) benötigst, wechsle zu python-jose oder Authlib. Manuelles Base64-Dekodieren funktioniert zum Debuggen, bietet aber keinerlei Sicherheitsgarantien.
Wann ich welche Option wähle: PyJWT für jeden Standard-Webdienst mit HS256- oder RS256-Verifikation. python-jose, wenn die Architektur verschlüsselte Tokens oder JWKS-Rotation umfasst. Manuelles Base64 für schnelle Inspektion in Umgebungen, in denen pip nicht verfügbar ist (CI-Container, eingeschränkte Produktionshosts, AWS-Lambda-Kaltstarts, bei denen du Abhängigkeiten minimieren möchtest). Authlib, wenn das Projekt es bereits für OAuth-Client-Flows verwendet und eine weitere JWT-Bibliothek redundant wäre.
Als Code-freie Alternative kannst du beliebige Tokens in den JWT Decoder einfügen und den dekodierten Header und Payload mit Claim-Validierungsfeedback einsehen.
Häufig gestellte Fragen
Wie dekodiere ich einen JWT in Python ohne Signaturverifikation?
Übergib options={"verify_signature": False} und algorithms=["HS256"] an jwt.decode(). Das gibt das Payload-Dict zurück, ohne die Signatur zu prüfen. Nutze das nur zur Inspektion — um Claims zu lesen, bevor du den richtigen öffentlichen Schlüssel abrufst, oder zum Debuggen in der Entwicklungsumgebung. Verzichte nie auf die Verifikation bei Tokens, die Zugriff auf irgendetwas schützen.
import jwt
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
payload = jwt.decode(
token,
options={"verify_signature": False},
algorithms=["HS256"]
)
print(payload)
# {'sub': 'usr_8f2a', 'role': 'admin'}Was ist der Unterschied zwischen PyJWT und python-jwt?
PyJWT (pip install PyJWT, import jwt) ist die beliebteste JWT-Bibliothek für Python mit über 80 Millionen monatlichen Downloads. python-jwt (pip install python_jwt) ist eine separate, deutlich weniger genutzte Bibliothek mit einer anderen API-Oberfläche. Wenn du import jwt im Code von jemandem siehst, verwenden sie PyJWT. Die Namensverwirrung entsteht dadurch, dass der PyPI-Paketname (PyJWT) vom Import-Namen (jwt) abweicht. Bleib bei PyJWT, sofern du keinen spezifischen Grund hast, davon abzuweichen.
Wie dekodiere ich einen JWT mit RS256 in Python?
Installiere sowohl PyJWT als auch das cryptography-Backend: pip install PyJWT cryptography. Übergib dann den PEM-kodierten öffentlichen Schlüssel als key-Argument und algorithms=["RS256"]. PyJWT delegiert die RSA-Signaturverifikation an die cryptography-Bibliothek. Ohne das installierte cryptography-Paket wirft PyJWT einen Fehler, wenn du RSA- oder EC-Algorithmen verwenden möchtest.
import jwt
public_key = open("public_key.pem").read()
payload = jwt.decode(
token,
public_key,
algorithms=["RS256"],
audience="https://api.example.com"
)Warum wirft PyJWT einen ExpiredSignatureError?
PyJWT prüft standardmäßig den exp-Claim (Ablaufzeit). Wenn die aktuelle UTC-Zeit den exp-Zeitstempel überschritten hat, wirft es jwt.ExpiredSignatureError. Du kannst Toleranz für Uhrabweichungen mit dem leeway-Parameter hinzufügen: jwt.decode(token, key, algorithms=["HS256"], leeway=timedelta(seconds=30)). Das gibt eine 30-Sekunden-Toleranzperiode. Um die Ablaufprüfung vollständig zu deaktivieren (nicht für die Produktion empfohlen), übergib 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")Kann ich JWT-Claims ohne eine Bibliothek in Python lesen?
Ja. Teile den Token an Punkten auf, nimm das zweite Segment (den Payload), füge =-Zeichen hinzu, damit die Länge ein Vielfaches von 4 ist, dekodiere es dann base64url und parse das JSON. Das gibt dir das Claims-Dict, verifiziert aber nicht die Signatur. Das ist nützlich in eingeschränkten Umgebungen, in denen PyJWT nicht installiert werden kann, oder für schnelle Debugging-Skripte.
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'}Wie validiere ich den audience-Claim mit PyJWT?
Übergib den audience-Parameter an jwt.decode(): jwt.decode(token, key, algorithms=["HS256"], audience="https://api.example.com"). PyJWT vergleicht den aud-Claim im Token mit dem von dir angegebenen Wert. Wenn der Token keinen aud-Claim hat oder der Wert nicht übereinstimmt, wirft es jwt.InvalidAudienceError. Du kannst auch eine Liste akzeptabler Audiences übergeben, wenn dein Dienst Tokens akzeptiert, die für mehrere APIs bestimmt sind.
import jwt
payload = jwt.decode(
token,
secret,
algorithms=["HS256"],
audience=["https://api.example.com", "https://admin.example.com"]
)Verwandte Tools
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.