JWT Decoder Python — Avkoda JWT med PyJWT

·DevOps Engineer & Python Automation Specialist·Granskad avMaria Santos·Publicerad

Använd det kostnadsfria JWT Decoder direkt i webbläsaren — ingen installation krävs.

Prova JWT Decoder online →

Varje API som använder tokenbaserad autentisering ger dig en JWT förr eller senare, och att ta reda på vad som finns inuti är en av de uppgifter som dyker upp ständigt under utveckling. En JWT-avkodare i Python tar den ogenomskinliga base64-strängen och omvandlar den till en läsbar claims-dictionary som du faktiskt kan arbeta med. PyPI-paketet du vill ha är PyJWT — installeras med pip install PyJWT men importeras som import jwt. Den här guiden går igenom jwt.decode() med fullständig signaturverifiering, avkodning utan hemlig nyckel för snabb inspektion, manuell base64-avkodning utan något bibliotek, RS256-verifiering med publik nyckel, och vanliga fallgropar jag stött på i produktions-auth-system. För en snabb engångskontroll gör den webbaserade JWT-avkodaren detta omedelbart utan någon kod. Alla exempel riktar sig till Python 3.10+ och PyJWT 2.x.

  • pip install PyJWT, sedan import jwt — paketnamnet och importnamnet är olika, vilket förvirrar nästan alla.
  • jwt.decode(token, key, algorithms=["HS256"]) returnerar en vanlig dict med claims. Skicka alltid algorithms explicit.
  • För att inspektera claims utan verifiering: jwt.decode(token, options={"verify_signature": False}, algorithms=["HS256"]).
  • För RSA/EC-tokens: pip install PyJWT cryptography — cryptography-bakgrunden krävs för asymmetriska algoritmer.
  • Manuell avkodning (base64 + json) fungerar utan något bibliotek men hoppar över all signatur- och utgångstidsvalidering.

Vad är JWT-avkodning?

En JSON Web Token består av tre base64url-kodade segment separerade med punkter: ett huvud (algoritm och tokentyp), en payload (claims — användar-ID, roller, utgångstid), och en signatur. Att avkoda en JWT innebär att extrahera huvud- och payload-segmenten, base64url-avkoda dem och tolka den resulterande JSON:en till en dictionary av claims.

Huvudet berättar vilken algoritm som användes för att signera token och innehåller ibland ett kid (nyckel-ID) för att hitta rätt verifieringsnyckel. Payload:en innehåller de faktiska uppgifterna: vem token utfärdades till (sub), när den går ut (exp), vilken tjänst den är avsedd för (aud), plus eventuella anpassade claims som din applikation definierar. Signatursegmentet bevisar att token inte manipulerats, men du behöver den hemliga nyckeln eller publika nyckeln för att verifiera den. Avkodning och verifiering är separata operationer. Du kan avkoda payload:en utan att verifiera signaturen (användbart för felsökning), men lita aldrig på overifierade claims för auktoriseringsbeslut.

Before · json
After · json
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxMTgxNTYwMH0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
{
  "sub": "usr_8f2a",
  "role": "admin",
  "exp": 1711815600
}

jwt.decode() — Avkoda och verifiera med PyJWT

jwt.decode() är den primära funktionen från PyJWT-biblioteket. Den tar den kodade tokensträngen, den hemliga nyckeln (för HMAC-algoritmer) eller publika nyckeln (för RSA/EC), och en obligatorisk algorithms-lista. Funktionen verifierar signaturen, kontrollerar standardclaims som exp och nbf, och returnerar payload:en som en Python-dictionary. Om något misslyckas — felaktig signatur, utgången token, fel algoritm — höjer den ett specifikt undantag.

Minimalt fungerande exempel

Python 3.10+
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"])
# admin

Parametern algorithms är en lista, inte en enskild sträng, och den är obligatorisk i PyJWT 2.x. Det är en säkerhetsfunktion: utan den kan en angripare skapa en token med alg: none i huvudet och kringgå verifieringen helt. Ange alltid exakt vilka algoritmer din applikation accepterar. Om du bara utfärdar HS256-tokens bör listan vara ["HS256"] — inte ["HS256", "RS256", "none"]. En snäv lista minskar attackytan.

En sak som förvirrade mig tidigt: PyJWT 2.x ändrade jwt.encode() till att returnera en sträng istället för bytes. Om du läser gamla Stack Overflow-svar som anropar .decode("utf-8") på den kodade token, är den koden från PyJWT 1.x-eran och kommer att kasta ett AttributeError på version 2.x. Token är redan en sträng — använd den direkt.

Fullständig rundtur med utgångstid

Python 3.10+ — encode then decode with exp
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")
Notering:PyJWT konverterar datetime-objekt till Unix-tidsstämplar automatiskt vid kodning. Vid avkodning returneras claims exp, iat och nbf som heltal, inte datetime-objekt. Du behöver konvertera dem själv med datetime.fromtimestamp(payload["exp"], tz=timezone.utc).

Avkoda en JWT utan signaturverifiering

Ibland behöver du läsa claims innan du kan verifiera token. Ett vanligt scenario: tokenhuvudet innehåller ett kid (nyckel-ID)-fält, och du behöver hämta den matchande publika nyckeln från en JWKS-endpoint innan du kan verifiera. PyJWT stöder detta med alternativet verify_signature: False. Du skickar fortfarande algorithms-listan, men key-argumentet ignoreras.

Python 3.10+ — unverified decode
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 endpoint
Varning:Overifierade tokens är inte tillförlitliga. Använd detta mönster enbart för routingbeslut (vilken nyckel som ska hämtas, vilken tenant som ska slås upp). Fatta aldrig auktoriseringsbeslut baserade på overifierade claims. Angriparen kan lägga vad som helst i payload:en.

Det finns en subtil distinktion här. jwt.get_unverified_header() läser bara huvudet — det första segmentet. Anropet till jwt.decode() med verify_signature: False läser payload:en (det andra segmentet). Tillsammans kan du extrahera allt från en token utan en nyckel. PyJWT validerar fortfarande att token har rätt struktur (tre punktseparerade segment, giltig base64, giltig JSON) även när signaturverifiering är avstängd. Om token är strukturellt felaktigt höjer den DecodeError oavsett vilka alternativ du skickar.

jwt.decode() parametrar — referens

Den fullständiga signaturen är jwt.decode(jwt, key, algorithms, options, audience, issuer, leeway, require). Alla parametrar efter algorithms är nyckelordsbaserade.

Parameter
Typ
Standard
Beskrivning
jwt
str | bytes
(obligatorisk)
Den kodade JWT-strängen som ska avkodas
key
str | bytes | dict
(obligatorisk)
Hemlig nyckel (HMAC) eller publik nyckel (RSA/EC) för verifiering
algorithms
list[str]
(obligatorisk)
Tillåtna algoritmer — t.ex. ["HS256"], ["RS256"]. Utelämna aldrig detta.
options
dict
{}
Åsidosätt verifieringsflaggor: verify_signature, verify_exp, verify_aud, m.fl.
audience
str | list[str]
None
Förväntad aud-claim — höjer InvalidAudienceError vid avvikelse
issuer
str
None
Förväntad iss-claim — höjer InvalidIssuerError vid avvikelse
leeway
timedelta | int
0
Sekunder av klocktolerans för kontroll av exp och nbf
require
list[str]
[]
Claims som måste finnas — höjer MissingRequiredClaimError annars

Dictionaryn options ger detaljerad kontroll över vilka valideringar PyJWT utför. Nycklarna mappas till individuella kontroller: verify_signature, verify_exp, verify_nbf, verify_iss, verify_aud och verify_iat. Alla är som standard True utom när du explicit sätter dem till False. I produktion, låt alla dessa vara kvar på sina standardvärden. Det enda tillfället jag inaktiverar enskilda kontroller är under utveckling när jag arbetar med gamla testtoken och tillfälligt behöver kringgå utgångstidskontrollen.

Python 3.10+ — using options and require
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
)

Manuell JWT-avkodning med base64 och json

Du kan avkoda en JWT-payload med enbart Python-standardbiblioteket — inget pip install behövs. Det är genuint användbart i flera situationer: felsökningsskript där det vore överdrivet att lägga till ett beroende, begränsade CI-miljöer där bara standardbiblioteket är tillgängligt, AWS Lambda-funktioner där du vill minimera starttid, eller helt enkelt för att förstå vad en JWT faktiskt är under huven. Processen är enkel: dela vid punkter, ta det segment du vill ha, lägg till base64-utfyllnad, avkoda, och tolka JSON:en.

Modulerna base64 och json finns båda i Python-standardbiblioteket, så det här tillvägagångssättet fungerar på alla Python-installationer från 3.6 och uppåt. Funktionerna nedan hanterar huvud (första segmentet) och payload (andra segmentet) separat:

Python 3.10+ — manual JWT decode without any library
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: admin

Utfyllnadstricket (+= "=" * (-len(s) % 4)) är det som alla glömmer. JWT base64url utelämnar avslutande =-tecken, men Pythons urlsafe_b64decode kräver dem. Utan utfyllnadskorrigeringen får du ett binascii.Error: Incorrect padding.

Notering:Manuell avkodning verifierar inte signaturen. Vem som helst kan ändra payload:en i en JWT och koda om den. Använd detta tillvägagångssätt enbart för inspektion och felsökning, aldrig för auktoriseringslogik. För allt som spelar roll, använd jwt.decode() med en riktig nyckel.

Avkoda JWT:er från API-svar och tokenfiler

De två vanligaste verkliga scenarierna: att extrahera en JWT från ett HTTP-svar (en OAuth-tokenendpoint, ett inloggnings-API) och att läsa tokens från filer (tjänstkontokredentialer, Kubernetes-monterade hemligheter, cachade tokens på disk). Båda kräver felhantering. Nätverksanrop misslyckas. Filer saknas. Tokens går ut mellan när de cachas och när de läses.

Exemplen nedan använder httpx för HTTP-anrop (byt mot requests om du föredrar det, mönstret är identiskt) och pathlib.Path för filoperationer. Varje exempel fångar specifika PyJWT-undantag snarare än ett generellt except Exception, så du kan reagera lämpligt på varje felläge: autentisera om vid utgång, larma vid signaturfel, försök igen vid nätverkstimeout.

Avkoda en JWT från ett API-svar

Python 3.10+ — decode JWT from OAuth token endpoint
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')}")

Avkoda en JWT från en fil

Python 3.10+ — read and decode a cached token 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:
    """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-avkodning från kommandoraden

Ibland behöver du bara kika på en token från terminalen utan att skriva ett skript. Kanske felsöker du ett OAuth-flöde och vill se vad som finns i Authorization-headern, eller så hämtade du en token från webbläsarens DevTools och vill kontrollera dess utgångstid. Pythons -c-flagga gör detta till en one-liner. Mata in token och få claims som formaterad JSON. Ingen skriptfil behövs, ingen virtuell miljö.

Bash
# 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"
# }
Bash
# 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"
# }
Bash
# 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"

För ett visuellt alternativ utan terminalinställningar, klistra in din token i den ToolDeck JWT-avkodaren och se huvud, payload och signaturverifieringsstatus direkt.

python-jose och andra alternativ

python-jose är ett alternativt JWT-bibliotek som stöder JWS, JWE (krypterade tokens) och JWK inbyggt. Om din applikation behöver hantera krypterade JWT:er (JWE) — där payload:en i sig är krypterad, inte bara signerad — är python-jose rätt val eftersom PyJWT inte stöder JWE alls. Biblioteket har också inbyggd JWKS-nyckelsethantering, vilket förenklar integration med identitetsleverantörer som Auth0, Okta eller Keycloak som exponerar roterande nyckelset. Avkodningsgränssnittet är nästan identiskt med PyJWT, så att byta mellan dem kräver minimala kodändringar:

Python 3.10+ — python-jose
# 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']}")

Min rekommendation: börja med PyJWT. Det täcker 95% av JWT-användningsfallen, har den största gemenskapen och API:et är välstrukturerat. Byt till python-jose om du behöver JWE-stöd eller föredrar dess JWKS-hantering. Ett tredje alternativ värt att nämna är Authlib, som buntar ihop JWT-hantering i ett mycket större OAuth/OIDC-ramverk. Om du redan använder Authlib för OAuth-klientflöden sparar dess authlib.jose.jwt-modul dig från att lägga till ett andra JWT-beroende. I övrigt är det ett tungt beroende enbart för tokenavkodning.

Terminalutdata med syntaxmarkering

Att läsa rå JWT-claims i en terminal fungerar för snabba kontroller, men när du felsöker tokenpayloads regelbundet (jag gjorde detta dagligen när jag byggde en intern auth-gateway i Stockholm) gör färglagd utdata verklig skillnad. Strängvärden, tal, booleaner och null renderas alla i distinkta färger, vilket innebär att du kan upptäcka en saknad behörighet eller fel utgångstidsstämpel på ett ögonblick utan att läsa varje tecken.

Biblioteket rich (pip install rich) har en print_json-funktion som tar antingen en JSON-sträng eller en Python-dict och skriver ut den med fullständig syntaxmarkering i terminalen. Kombinera det med PyJWT för ett tvåradigt JWT-inspektionsarbetsflöde:

Python 3.10+ — rich terminal output
# 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
# }
Notering:rich-utdata innehåller ANSI-escape-koder. Skriv inte det till filer eller returnera det från API-endpoints — det är enbart för terminalvisning. Använd json.dumps() när du behöver oformaterad textutdata.

Arbeta med stora tokenmängder

JWT-tokens i sig är små (typiskt under 2 KB styck), men det finns scenarier där du bearbetar dem i bulk. Analyser av granskningsloggar efter en säkerhetsincident. Sessionsmigreringsskript när du byter auth-leverantör. Efterlevnadsbatchvalidering där du behöver bevisa att varje token som utfärdades under de senaste 90 dagarna signerades med rätt nyckel. Om du har tiotusentals tokens i en NDJSON-loggfil undviker rad-för-rad-bearbetning att läsa in hela filen i minnet och låter dig rapportera resultat inkrementellt.

Batchvalidera tokens från en granskningslogg

Python 3.10+ — streaming token validation
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: 17

Extrahera claims från NDJSON-tokenexport

Python 3.10+ — extract and transform claims from token log
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")
Notering:För filer under några hundra MB är rad-för-rad-läsning tillräckligt effektivt. Om du stöter på prestandagränser för mycket stora tokendumpar, överväg att använda multiprocessing.Pool för att fördela valideringen över kärnor, eftersom varje token är oberoende.

Vanliga misstag

Dessa fyra misstag dyker upp upprepade gånger i kodgranskningar och Stack Overflow-frågor. Vart och ett är lätt att göra, och felmeddelandet PyJWT ger dig pekar inte alltid direkt på orsaken. Jag har sett paketnamnsproblemet ensamt kosta timmar av felsökningstid när någon installerar fel bibliotek och får ett helt oväntat API.

Förväxla PyJWT och python-jwt paketnamn

Problem: Att köra pip install jwt eller pip install python-jwt installerar ett helt annat paket. import jwt misslyckas då eller ger dig ett API du inte känner igen.

Lösning: Installera alltid med pip install PyJWT. Importen är import jwt. Kontrollera med pip show PyJWT för att bekräfta rätt paket.

Before · Python
After · Python
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
Utelämna algorithms-parametern

Problem: I PyJWT 1.x var algorithms valfritt och tillät som standard vilken algoritm som helst. Det skapade en säkerhetsrisk där en angripare kunde sätta alg: none. PyJWT 2.x höjer nu DecodeError om algorithms saknas.

Lösning: Skicka alltid algorithms som en explicit lista. Använd enbart de algoritmer din applikation faktiskt utfärdar tokens med.

Before · Python
After · Python
# 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"])
Använda jwt.decode() med fel nyckeltyp för RS256

Problem: Att skicka en strängsecret till jwt.decode() med algorithms=["RS256"] höjer InvalidSignatureError. RS256 kräver en PEM-kodad publik nyckel, inte en delad hemlighets-sträng.

Lösning: Läs in den PEM-publika nyckeln från en fil eller miljövariabel. Installera cryptography-paketet: pip install PyJWT cryptography.

Before · Python
After · Python
# 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"])
Glömma base64-utfyllnad vid manuell avkodning

Problem: JWT base64url-kodning tar bort avslutande =-tecken. Pythons base64.urlsafe_b64decode höjer binascii.Error: Incorrect padding om du skickar rå segment utan att korrigera utfyllnaden.

Lösning: Lägg till utfyllnad innan avkodning: segment += '=' * (-len(segment) % 4). Den här formeln producerar alltid rätt antal utfyllnadstecken (0, 1, 2 eller 3).

Before · Python
After · Python
import base64
payload_b64 = token.split(".")[1]
data = base64.urlsafe_b64decode(payload_b64)
# binascii.Error: Incorrect padding
import base64
payload_b64 = token.split(".")[1]
payload_b64 += "=" * (-len(payload_b64) % 4)
data = base64.urlsafe_b64decode(payload_b64)  # works

PyJWT vs alternativ — snabbjämförelse

Metod
Verifierar signatur
Validerar claims
Anpassade typer
Kräver installation
PyJWT jwt.decode()
✓ (exp, aud, iss, nbf)
Ej tillämpligt (returnerar dict)
pip install PyJWT
PyJWT overifierad avkodning
Ej tillämpligt
pip install PyJWT
Manuell base64-avkodning
Ej tillämpligt
Nej (stdlib)
python-jose jwt.decode()
Ej tillämpligt
pip install python-jose
Authlib jwt.decode()
Ej tillämpligt
pip install Authlib
PyJWT + cryptography
✓ (RSA/EC)
Ej tillämpligt
pip install PyJWT cryptography

PyJWT är rätt startpunkt för de flesta Python-applikationer. Det täcker HMAC och (med cryptography-bakgrunden) RSA- och EC-signaturverifiering. Om du behöver JWE (krypterade tokens), byt till python-jose eller Authlib. Manuell base64-avkodning fungerar för felsökning men ger noll säkerhetsgarantier.

Så här väljer jag: PyJWT för alla standardwebbtjänster som gör HS256- eller RS256-verifiering. python-jose när arkitekturen inkluderar krypterade tokens eller JWKS-rotation. Manuell base64 för snabb inspektion i miljöer där pip inte är tillgängligt (CI-containrar, begränsade produktionsvärdar, AWS Lambda-kallstarter där du vill minimera beroenden). Authlib när projektet redan använder det för OAuth-klientflöden och ett ytterligare JWT-bibliotek vore redundant.

För ett kodfritt alternativ, klistra in valfri token i den JWT-avkodaren för att se det avkodade huvudet och payload:en med claim-valideringsfeedback.

Vanliga frågor

Hur avkodar jag en JWT i Python utan att verifiera signaturen?

Skicka options={"verify_signature": False} och algorithms=["HS256"] till jwt.decode(). Detta returnerar payload-dictionaryn utan att kontrollera signaturen. Använd detta enbart för inspektion — för att läsa claims innan du hämtar rätt publik nyckel, eller för felsökning under utveckling. Hoppa aldrig över verifiering för tokens som styr åtkomst till något.

Python
import jwt

token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

payload = jwt.decode(
    token,
    options={"verify_signature": False},
    algorithms=["HS256"]
)
print(payload)
# {'sub': 'usr_8f2a', 'role': 'admin'}

Vad är skillnaden mellan PyJWT och python-jwt?

PyJWT (pip install PyJWT, import jwt) är det mest populära JWT-biblioteket för Python med över 80 miljoner månadsvis nedladdningar. python-jwt (pip install python_jwt) är ett separat, mycket mindre använt bibliotek med en annan API-yta. Om du ser import jwt i någons kod använder de PyJWT. Namnförvirringen uppstår eftersom PyPI-paketnamnet (PyJWT) skiljer sig från importnamnet (jwt). Håll dig till PyJWT om du inte har ett specifikt skäl att inte göra det.

Hur avkodar jag en JWT med RS256 i Python?

Installera både PyJWT och cryptography-bakgrunden: pip install PyJWT cryptography. Skicka sedan den PEM-kodade publika nyckeln som key-argument och algorithms=["RS256"]. PyJWT delegerar RSA-signaturverifieringen till cryptography-biblioteket. Utan cryptography-paketet installerat höjer PyJWT ett fel när du försöker använda RSA- eller EC-algoritmer.

Python
import jwt

public_key = open("public_key.pem").read()

payload = jwt.decode(
    token,
    public_key,
    algorithms=["RS256"],
    audience="https://api.example.com"
)

Varför höjer PyJWT ExpiredSignatureError?

PyJWT kontrollerar exp-claimet (utgångstid) som standard. Om den aktuella UTC-tiden har passerat exp-tidsstämpeln höjer den jwt.ExpiredSignatureError. Du kan lägga till klocktolerans med leeway-parametern: jwt.decode(token, key, algorithms=["HS256"], leeway=timedelta(seconds=30)). Det ger en nådetid på 30 sekunder. För att inaktivera utgångskontroll helt (rekommenderas inte i produktion), skicka options={"verify_exp": False}.

Python
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")

Kan jag läsa JWT-claims utan något bibliotek i Python?

Ja. Dela token vid punkter, ta det andra segmentet (payload), fyll ut det med =-tecken för att göra längden till en multipel av 4, avkoda det med base64url och tolka JSON-resultatet. Det ger dig claims-dictionaryn men verifierar inte signaturen. Det är användbart i begränsade miljöer där du inte kan installera PyJWT, eller för snabba felsökningsskript.

Python
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'}

Hur validerar jag audience-claimet med PyJWT?

Skicka audience-parametern till jwt.decode(): jwt.decode(token, key, algorithms=["HS256"], audience="https://api.example.com"). PyJWT jämför aud-claimet i token mot det värde du anger. Om token saknar aud-claim, eller om värdet inte stämmer, höjs jwt.InvalidAudienceError. Du kan också skicka en lista med godkända audiences om din tjänst accepterar tokens avsedda för flera API:er.

Python
import jwt

payload = jwt.decode(
    token,
    secret,
    algorithms=["HS256"],
    audience=["https://api.example.com", "https://admin.example.com"]
)

Relaterade verktyg

DV
Dmitri VolkovDevOps Engineer & Python Automation Specialist

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.

MS
Maria SantosTeknisk granskare

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.