JWT Decoder Python โ Panduan Decode JWT dengan PyJWT
Gunakan JWT Decoder gratis langsung di browser Anda โ tidak perlu instalasi.
Coba JWT Decoder Online โSetiap API yang menggunakan autentikasi berbasis token akan memberikan JWT pada suatu titik, dan mengetahui isinya adalah salah satu tugas yang selalu muncul selama pengembangan. Dekoder JWT di Python mengubah string base64 yang tidak terbaca itu menjadi kamus claims yang dapat Anda gunakan langsung. Paket PyPI yang Anda butuhkan adalah PyJWT โ dipasang dengan pip install PyJWT tetapi diimpor sebagai import jwt. Panduan ini membahas jwt.decode() dengan verifikasi tanda tangan penuh, mendekode tanpa kunci rahasia untuk inspeksi cepat, dekode base64 manual tanpa pustaka apa pun, verifikasi kunci publik RS256, dan kesalahan umum yang sering dijumpai dalam sistem autentikasi produksi. Untuk pemeriksaan sekali pakai yang cepat, JWT Decoder online melakukan ini seketika tanpa kode apa pun. Semua contoh menargetkan Python 3.10+ dan PyJWT 2.x.
- โpip install PyJWT, kemudian import jwt โ nama paket dan nama impor berbeda, hal ini hampir selalu membingungkan.
- โjwt.decode(token, key, algorithms=["HS256"]) mengembalikan dict biasa berisi claims. Selalu berikan algorithms secara eksplisit.
- โUntuk memeriksa claims tanpa verifikasi: jwt.decode(token, options={"verify_signature": False}, algorithms=["HS256"]).
- โUntuk token RSA/EC: pip install PyJWT cryptography โ backend cryptography diperlukan untuk algoritma asimetris.
- โDekode manual (base64 + json) berfungsi tanpa pustaka apa pun tetapi melewati semua validasi tanda tangan dan kedaluwarsa.
Apa itu Dekode JWT?
JSON Web Token terdiri dari tiga segmen yang di-encode base64url dan dipisahkan oleh tanda titik: header (algoritma dan tipe token), payload (claims โ ID pengguna, peran, waktu kedaluwarsa), dan tanda tangan. Mendekode JWT berarti mengekstrak segmen header dan payload, mendekode base64url-nya, dan mengurai JSON yang dihasilkan menjadi kamus claims.
Header memberi tahu Anda algoritma mana yang digunakan untuk menandatangani token dan terkadang menyertakan kid (ID kunci) untuk menemukan kunci verifikasi yang tepat. Payload menyimpan data sebenarnya: kepada siapa token diterbitkan (sub), kapan kedaluwarsa (exp), layanan mana yang dituju (aud), ditambah claims kustom yang didefinisikan oleh aplikasi Anda. Segmen tanda tangan membuktikan bahwa token tidak dimanipulasi, tetapi Anda memerlukan kunci rahasia atau kunci publik untuk memverifikasinya. Dekode dan verifikasi adalah operasi yang terpisah. Anda dapat mendekode payload tanpa memverifikasi tanda tangan (berguna untuk debugging), tetapi jangan pernah mempercayai claims yang belum diverifikasi untuk keputusan otorisasi.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxMTgxNTYwMH0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
{
"sub": "usr_8f2a",
"role": "admin",
"exp": 1711815600
}jwt.decode() โ Dekode dan Verifikasi dengan PyJWT
jwt.decode() adalah fungsi utama dari pustaka PyJWT. Fungsi ini menerima string token yang di-encode, kunci rahasia (untuk algoritma HMAC) atau kunci publik (untuk RSA/EC), dan daftar algorithms yang wajib diisi. Fungsi ini memverifikasi tanda tangan, memeriksa claims standar seperti exp dan nbf, dan mengembalikan payload sebagai kamus Python. Jika ada yang gagal โ tanda tangan salah, token kedaluwarsa, algoritma tidak sesuai โ fungsi ini menaikkan exception tertentu.
Contoh Minimal yang Berfungsi
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"])
# adminParameter algorithms adalah sebuah daftar, bukan string tunggal, dan wajib diisi di PyJWT 2.x. Ini adalah fitur keamanan: tanpanya, penyerang dapat membuat token dengan alg: none di header dan melewati verifikasi sepenuhnya. Selalu tentukan dengan tepat algoritma mana yang diterima aplikasi Anda. Jika Anda hanya menerbitkan token HS256, daftarnya harus ["HS256"] โ bukan ["HS256", "RS256", "none"]. Menjaga daftar tetap ketat mengurangi permukaan serangan.
Hal yang membingungkan di awal: PyJWT 2.x mengubah jwt.encode() agar mengembalikan string, bukan bytes. Jika Anda membaca jawaban Stack Overflow lama yang memanggil .decode("utf-8") pada token yang di-encode, kode itu berasal dari era PyJWT 1.x dan akan menaikkan AttributeError di versi 2.x. Token sudah berupa string โ gunakan langsung saja.
Siklus Penuh dengan Kedaluwarsa
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")datetime ke Unix timestamp secara otomatis saat encoding. Saat decoding, claims exp, iat, dan nbf dikembalikan sebagai bilangan bulat, bukan objek datetime. Anda perlu mengonversinya sendiri dengan datetime.fromtimestamp(payload["exp"], tz=timezone.utc).Dekode JWT Tanpa Verifikasi Tanda Tangan
Terkadang Anda perlu membaca claims sebelum dapat memverifikasi token. Skenario umum: header token berisi field kid (ID kunci), dan Anda perlu mengambil kunci publik yang sesuai dari endpoint JWKS sebelum dapat melakukan verifikasi. PyJWT mendukung ini dengan opsi verify_signature: False. Anda tetap memberikan daftar algorithms, tetapi argumen key diabaikan.
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 endpointAda perbedaan halus di sini. jwt.get_unverified_header() hanya membaca header โ segmen pertama. Panggilan jwt.decode() dengan verify_signature: False membaca payload (segmen kedua). Dengan keduanya, Anda dapat mengekstrak segalanya dari token tanpa kunci. PyJWT tetap memvalidasi bahwa token memiliki struktur yang benar (tiga segmen yang dipisahkan titik, base64 valid, JSON valid) meskipun verifikasi tanda tangan dimatikan. Jika token secara struktural tidak valid, ia menaikkan DecodeError terlepas dari opsi yang Anda berikan.
Referensi Parameter jwt.decode()
Tanda tangan lengkapnya adalah jwt.decode(jwt, key, algorithms, options, audience, issuer, leeway, require). Semua parameter setelah algorithms hanya dapat digunakan sebagai keyword argument.
Dict options memberikan kendali lebih detail atas validasi yang dilakukan PyJWT. Kuncinya memetakan ke pemeriksaan individual: verify_signature, verify_exp, verify_nbf, verify_iss, verify_aud, dan verify_iat. Semua bernilai default True kecuali Anda secara eksplisit mengaturnya ke False. Dalam produksi, biarkan semua ini pada nilai defaultnya. Satu-satunya waktu saya menonaktifkan pemeriksaan individual adalah selama pengembangan ketika bekerja dengan token pengujian yang sudah kedaluwarsa dan perlu melewati pemeriksaan kedaluwarsa sementara.
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
)Dekode JWT Manual dengan base64 dan json
Anda dapat mendekode payload JWT hanya menggunakan pustaka standar Python โ tanpa pip install sama sekali. Ini benar-benar berguna dalam beberapa situasi: skrip debugging di mana menambahkan dependensi terlalu berlebihan, lingkungan CI terbatas di mana hanya pustaka standar yang tersedia, fungsi AWS Lambda di mana Anda ingin meminimalkan waktu cold start, atau sekadar memahami apa sebenarnya JWT di balik layar. Prosesnya sederhana: pisahkan berdasarkan titik, ambil segmen yang Anda inginkan, tambahkan padding base64, dekode, dan urai JSON-nya.
Modul base64 dan json keduanya ada di pustaka standar Python, sehingga pendekatan ini berfungsi pada instalasi Python apa pun mulai dari versi 3.6 ke atas. Fungsi-fungsi di bawah menangani header (segmen pertama) dan payload (segmen kedua) secara terpisah:
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: adminTrik padding (+= "=" * (-len(s) % 4)) adalah bagian yang selalu dilupakan. base64url JWT menghapus karakter = di akhir, tetapi urlsafe_b64decode milik Python memerlukannya. Tanpa perbaikan padding, Anda akan mendapat binascii.Error: Incorrect padding.
jwt.decode() dengan kunci yang sesungguhnya.Dekode JWT dari Respons API dan Berkas Token
Dua skenario dunia nyata yang paling umum: mengekstrak JWT dari respons HTTP (endpoint token OAuth, API login), dan membaca token dari berkas (kredensial service account, secret yang di-mount Kubernetes, token yang disimpan di disk). Keduanya memerlukan penanganan error yang tepat. Permintaan jaringan bisa gagal. Berkas bisa hilang. Token bisa kedaluwarsa antara saat disimpan dan saat dibaca.
Contoh di bawah menggunakan httpx untuk panggilan HTTP (ganti dengan requests jika Anda lebih suka, polanya identik) dan pathlib.Path untuk operasi berkas. Setiap contoh menangkap exception PyJWT yang spesifik alih-alih except Exception yang umum, sehingga Anda dapat merespons setiap mode kegagalan dengan tepat: autentikasi ulang saat kedaluwarsa, beri peringatan saat tanda tangan gagal, coba ulang saat timeout jaringan.
Dekode JWT dari Respons API
import jwt
import httpx # or requests
TOKEN_ENDPOINT = "https://auth.example.com/oauth/token"
SECRET_KEY = "shared-webhook-signing-key"
def get_and_decode_token() -> dict:
"""Fetch an access token from the auth server and decode it."""
try:
response = httpx.post(
TOKEN_ENDPOINT,
data={
"grant_type": "client_credentials",
"client_id": "svc_order_processor",
"client_secret": "cs_9f3a7b2e",
},
timeout=10.0,
)
response.raise_for_status()
except httpx.HTTPError as exc:
raise RuntimeError(f"Token request failed: {exc}") from exc
token_data = response.json()
access_token = token_data["access_token"]
try:
payload = jwt.decode(
access_token,
SECRET_KEY,
algorithms=["HS256"],
audience="https://api.example.com",
)
return payload
except jwt.InvalidTokenError as exc:
raise RuntimeError(f"Invalid token from auth server: {exc}") from exc
claims = get_and_decode_token()
print(f"Service: {claims['sub']}, Scopes: {claims.get('scope', 'none')}")Dekode JWT dari Berkas
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']}")Dekode JWT via Baris Perintah
Terkadang Anda hanya perlu melihat isi token dari terminal tanpa menulis skrip. Mungkin Anda sedang debugging alur OAuth dan ingin melihat apa yang ada di header Authorization, atau Anda mengambil token dari browser DevTools dan ingin memeriksa waktu kedaluwarsa-nya. Flag -c Python menjadikan ini satu baris perintah. Kirim token dan dapatkan claims sebagai JSON yang terformat. Tidak perlu berkas skrip, tidak perlu virtual environment.
# 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"Sebagai alternatif visual tanpa pengaturan terminal apa pun, tempelkan token Anda ke JWT Decoder ToolDeck dan lihat header, payload, serta status verifikasi tanda tangan seketika.
python-jose dan Alternatif Lain
python-jose adalah pustaka JWT alternatif yang mendukung JWS, JWE (token terenkripsi), dan JWK secara bawaan. Jika aplikasi Anda perlu menangani JWT terenkripsi (JWE) โ di mana payload itu sendiri dienkripsi, bukan hanya ditandatangani โ python-jose adalah pilihan yang tepat karena PyJWT sama sekali tidak mendukung JWE. Pustaka ini juga memiliki penanganan set kunci JWKS bawaan, yang menyederhanakan integrasi dengan penyedia identitas seperti Auth0, Okta, atau Keycloak yang memaparkan set kunci yang berputar. Antarmuka decode-nya hampir identik dengan PyJWT, sehingga berpindah di antara keduanya memerlukan perubahan kode minimal:
# 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']}")Rekomendasi saya: mulai dengan PyJWT. Ini mencakup 95% kasus penggunaan JWT, memiliki komunitas terbesar, dan API-nya bersih. Beralih ke python-jose jika Anda memerlukan dukungan JWE atau lebih menyukai penanganan JWKS-nya. Opsi ketiga yang layak disebutkan adalah Authlib, yang menggabungkan penanganan JWT di dalam framework OAuth/OIDC yang jauh lebih besar. Jika Anda sudah menggunakan Authlib untuk alur klien OAuth, modul authlib.jose.jwt-nya menghindarkan Anda dari menambahkan dependensi JWT kedua. Jika tidak, ini adalah dependensi yang terlalu berat hanya untuk dekode token.
Output Terminal dengan Penyorotan Sintaks
Membaca claims JWT mentah di terminal sudah cukup untuk pemeriksaan cepat, tetapi ketika Anda sering debugging payload token (saya melakukan ini setiap hari saat membangun gateway autentikasi internal), output berwarna membuat perbedaan nyata. Nilai string, angka, boolean, dan null semua ditampilkan dengan warna yang berbeda, sehingga Anda dapat langsung melihat izin yang hilang atau timestamp kedaluwarsa yang salah tanpa membaca setiap karakter.
Pustaka rich (pip install rich) memiliki fungsi print_json yang menerima string JSON atau dict Python dan mencetaknya dengan penyorotan sintaks penuh ke terminal. Gabungkan dengan PyJWT untuk alur inspeksi JWT dua baris:
# 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 mengandung kode escape ANSI. Jangan tulis ke berkas atau kembalikan dari endpoint API โ ini hanya untuk tampilan terminal. Gunakan json.dumps() ketika Anda memerlukan output teks biasa.Bekerja dengan Batch Token Besar
Token JWT sendiri berukuran kecil (biasanya di bawah 2 KB), tetapi ada skenario di mana Anda memprosesnya secara massal. Analisis log audit setelah insiden keamanan. Skrip migrasi sesi saat berpindah penyedia autentikasi. Validasi batch untuk kepatuhan di mana Anda perlu membuktikan bahwa setiap token yang diterbitkan dalam 90 hari terakhir ditandatangani dengan kunci yang benar. Jika Anda memiliki puluhan ribu token dalam berkas log NDJSON, memprosesnya baris per baris menghindari pemuatan seluruh berkas ke memori dan memungkinkan Anda melaporkan hasilnya secara bertahap.
Validasi Batch Token dari Log Audit
import jwt
import json
from pathlib import Path
SECRET_KEY = "audit-log-signing-key"
def validate_token_log(log_path: str) -> dict:
"""Process an NDJSON file where each line has a 'token' field.
Returns counts of valid, expired, and invalid tokens.
"""
stats = {"valid": 0, "expired": 0, "invalid": 0}
with open(log_path) as fh:
for line_num, line in enumerate(fh, 1):
line = line.strip()
if not line:
continue
try:
record = json.loads(line)
token = record["token"]
except (json.JSONDecodeError, KeyError):
stats["invalid"] += 1
continue
try:
jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
stats["valid"] += 1
except jwt.ExpiredSignatureError:
stats["expired"] += 1
except jwt.InvalidTokenError:
stats["invalid"] += 1
return stats
result = validate_token_log("auth-events-2026-03.ndjson")
print(f"Valid: {result['valid']}, Expired: {result['expired']}, Invalid: {result['invalid']}")
# Valid: 14832, Expired: 291, Invalid: 17Ekstrak Claims dari Ekspor Token NDJSON
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 untuk mendistribusikan validasi ke beberapa core, karena setiap token bersifat independen.Kesalahan Umum
Empat kesalahan ini sering muncul dalam tinjauan kode dan pertanyaan Stack Overflow. Masing-masing mudah dilakukan, dan pesan error yang diberikan PyJWT tidak selalu langsung menunjuk ke penyebabnya. Saya pernah melihat masalah penamaan paket saja membuang berjam-jam waktu debugging ketika seseorang memasang pustaka yang salah dan mendapat API yang sama sekali tidak dikenal.
Masalah: Menjalankan pip install jwt atau pip install python-jwt menginstal paket yang sama sekali berbeda. import jwt kemudian gagal atau memberikan API yang tidak Anda kenali.
Solusi: Selalu instal dengan pip install PyJWT. Impornya adalah import jwt. Periksa dengan pip show PyJWT untuk memastikan paket yang benar terpasang.
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
Masalah: Di PyJWT 1.x, algorithms bersifat opsional dan secara default mengizinkan algoritma apa pun. Ini menciptakan kerentanan keamanan di mana penyerang dapat mengatur alg: none. PyJWT 2.x sekarang menaikkan DecodeError jika algorithms tidak ada.
Solusi: Selalu berikan algorithms sebagai daftar eksplisit. Gunakan hanya algoritma yang benar-benar diterbitkan oleh aplikasi Anda.
# 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"])
Masalah: Memberikan string rahasia ke jwt.decode() dengan algorithms=["RS256"] menaikkan InvalidSignatureError. RS256 memerlukan kunci publik yang di-encode PEM, bukan string rahasia bersama.
Solusi: Muat kunci publik PEM dari berkas atau variabel lingkungan. Instal paket cryptography: pip install PyJWT cryptography.
# This fails โ RS256 needs a public key, not a string secret payload = jwt.decode(token, "my-secret", algorithms=["RS256"]) # InvalidSignatureError
public_key = open("public_key.pem").read()
payload = jwt.decode(token, public_key, algorithms=["RS256"])Masalah: Encoding base64url JWT menghapus karakter = di akhir. base64.urlsafe_b64decode Python menaikkan binascii.Error: Incorrect padding jika Anda memberikan segmen mentah tanpa memperbaiki padding.
Solusi: Tambahkan padding sebelum dekode: segment += '=' * (-len(segment) % 4). Rumus ini selalu menghasilkan jumlah karakter padding yang tepat (0, 1, 2, atau 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 Alternatif โ Perbandingan Singkat
PyJWT adalah titik awal yang tepat untuk sebagian besar aplikasi Python. Ini mencakup HMAC dan (dengan backend cryptography) verifikasi tanda tangan RSA dan EC. Jika Anda memerlukan JWE (token terenkripsi), beralih ke python-jose atau Authlib. Dekode base64 manual berfungsi untuk debugging tetapi tidak memberikan jaminan keamanan apa pun.
Kapan saya menggunakan masing-masing: PyJWT untuk layanan web standar yang melakukan verifikasi HS256 atau RS256. python-jose ketika arsitektur mencakup token terenkripsi atau rotasi JWKS. base64 manual untuk inspeksi cepat di lingkungan di mana pip tidak tersedia (container CI, host produksi terbatas, cold start AWS Lambda di mana Anda ingin meminimalkan dependensi). Authlib ketika proyek sudah menggunakannya untuk alur klien OAuth dan menambahkan pustaka JWT lain akan menjadi duplikasi.
Sebagai alternatif tanpa kode, tempelkan token apa pun ke JWT Decoder untuk melihat header dan payload yang telah didekode beserta umpan balik validasi claims.
Pertanyaan yang Sering Diajukan
Bagaimana cara mendekode JWT di Python tanpa memverifikasi tanda tangan?
Berikan options={"verify_signature": False} dan algorithms=["HS256"] ke jwt.decode(). Fungsi ini mengembalikan dict payload tanpa memeriksa tanda tangan. Gunakan ini hanya untuk inspeksi โ membaca claims sebelum mengambil kunci publik yang tepat, atau untuk debugging selama pengembangan. Jangan pernah melewati verifikasi pada token yang mengontrol akses ke sumber daya apa pun.
import jwt
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
payload = jwt.decode(
token,
options={"verify_signature": False},
algorithms=["HS256"]
)
print(payload)
# {'sub': 'usr_8f2a', 'role': 'admin'}Apa perbedaan antara PyJWT dan python-jwt?
PyJWT (pip install PyJWT, import jwt) adalah pustaka JWT paling populer untuk Python dengan lebih dari 80 juta unduhan bulanan. python-jwt (pip install python_jwt) adalah pustaka terpisah yang jauh lebih jarang digunakan dengan API yang berbeda. Jika Anda melihat import jwt dalam kode seseorang, mereka menggunakan PyJWT. Kebingungan penamaan ini muncul karena nama paket PyPI (PyJWT) berbeda dari nama impor (jwt). Gunakan PyJWT kecuali ada alasan khusus untuk tidak melakukannya.
Bagaimana cara mendekode JWT dengan RS256 di Python?
Instal PyJWT dan backend cryptography: pip install PyJWT cryptography. Kemudian berikan kunci publik yang di-encode PEM sebagai argumen key dan algorithms=["RS256"]. PyJWT mendelegasikan verifikasi tanda tangan RSA ke pustaka cryptography. Tanpa paket cryptography yang terpasang, PyJWT menaikkan error saat Anda mencoba menggunakan algoritma RSA atau EC.
import jwt
public_key = open("public_key.pem").read()
payload = jwt.decode(
token,
public_key,
algorithms=["RS256"],
audience="https://api.example.com"
)Mengapa PyJWT menaikkan ExpiredSignatureError?
PyJWT memeriksa claim exp (kedaluwarsa) secara default. Jika waktu UTC saat ini melewati timestamp exp, ia menaikkan jwt.ExpiredSignatureError. Anda dapat menambahkan toleransi perbedaan jam dengan parameter leeway: jwt.decode(token, key, algorithms=["HS256"], leeway=timedelta(seconds=30)). Ini memberikan tenggang waktu 30 detik. Untuk menonaktifkan pemeriksaan kedaluwarsa sepenuhnya (tidak disarankan untuk produksi), berikan 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")Bisakah saya membaca claims JWT tanpa pustaka apa pun di Python?
Ya. Pisahkan token menggunakan tanda titik, ambil segmen kedua (payload), tambahkan karakter = agar panjangnya menjadi kelipatan 4, kemudian dekode base64url dan urai JSON-nya. Ini menghasilkan dict claims tetapi tidak memverifikasi tanda tangan. Berguna di lingkungan terbatas di mana Anda tidak dapat menginstal PyJWT, atau untuk skrip debugging cepat.
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'}Bagaimana cara memvalidasi claim audience dengan PyJWT?
Berikan parameter audience ke jwt.decode(): jwt.decode(token, key, algorithms=["HS256"], audience="https://api.example.com"). PyJWT membandingkan claim aud dalam token dengan nilai yang Anda berikan. Jika token tidak memiliki claim aud, atau nilainya tidak cocok, ia menaikkan jwt.InvalidAudienceError. Anda juga dapat memberikan daftar audience yang dapat diterima jika layanan Anda menerima token yang ditujukan untuk beberapa API.
import jwt
payload = jwt.decode(
token,
secret,
algorithms=["HS256"],
audience=["https://api.example.com", "https://admin.example.com"]
)Alat Terkait
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.