JWT Decoder Python โ€” Panduan Decode JWT dengan PyJWT

ยทDevOps Engineer & Python Automation SpecialistยทDitinjau olehMaria SantosยทDiterbitkan

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.

Before ยท json
After ยท json
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

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

Parameter 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

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")
Catatan:PyJWT mengonversi objek 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.

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
Peringatan:Token yang belum diverifikasi tidak dapat dipercaya. Gunakan pola ini hanya untuk keputusan perutean (kunci mana yang harus diambil, tenant mana yang harus dicari). Jangan pernah membuat keputusan otorisasi berdasarkan claims yang belum diverifikasi. Penyerang dapat memasukkan apa saja yang mereka inginkan ke dalam payload.

Ada 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.

Parameter
Tipe
Default
Deskripsi
jwt
str | bytes
(wajib)
String JWT yang telah di-encode untuk didekode
key
str | bytes | dict
(wajib)
Kunci rahasia (HMAC) atau kunci publik (RSA/EC) untuk verifikasi
algorithms
list[str]
(wajib)
Algoritma yang diizinkan โ€” misalnya ["HS256"], ["RS256"]. Jangan pernah dihilangkan.
options
dict
{}
Mengganti flag verifikasi: verify_signature, verify_exp, verify_aud, dll.
audience
str | list[str]
None
Nilai aud claim yang diharapkan โ€” menaikkan InvalidAudienceError jika tidak sesuai
issuer
str
None
Nilai iss claim yang diharapkan โ€” menaikkan InvalidIssuerError jika tidak sesuai
leeway
timedelta | int
0
Toleransi perbedaan jam dalam detik untuk pemeriksaan exp dan nbf
require
list[str]
[]
Claim yang harus ada โ€” menaikkan MissingRequiredClaimError jika tidak ada

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.

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
)

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:

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

Trik 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.

Catatan:Dekode manual tidak memverifikasi tanda tangan. Siapa pun dapat mengubah payload JWT dan meng-encode-nya ulang. Gunakan pendekatan ini hanya untuk inspeksi dan debugging, jangan untuk logika otorisasi. Untuk hal-hal yang penting, gunakan 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

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')}")

Dekode JWT dari Berkas

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']}")

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.

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"

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:

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']}")

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:

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
# }
Catatan:Output 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

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

Ekstrak Claims dari Ekspor Token NDJSON

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")
Catatan:Untuk berkas berukuran hingga beberapa ratus MB, pembacaan baris per baris sudah cukup efisien. Jika Anda mencapai batas performa pada dump token yang sangat besar, pertimbangkan menggunakan 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.

โŒ Bingung dengan nama paket PyJWT dan python-jwt

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.

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
โŒ Menghilangkan parameter algorithms

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.

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"])
โŒ Menggunakan jwt.decode() dengan tipe kunci yang salah untuk RS256

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.

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"])
โŒ Lupa padding base64 saat dekode manual

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).

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 Alternatif โ€” Perbandingan Singkat

Metode
Verifikasi Tanda Tangan
Validasi Claims
Tipe Kustom
Perlu Instalasi
PyJWT jwt.decode()
โœ“
โœ“ (exp, aud, iss, nbf)
N/A (mengembalikan dict)
pip install PyJWT
PyJWT unverified decode
โœ—
โœ—
N/A
pip install PyJWT
Dekode base64 manual
โœ—
โœ—
N/A
Tidak (stdlib)
python-jose jwt.decode()
โœ“
โœ“
N/A
pip install python-jose
Authlib jwt.decode()
โœ“
โœ“
N/A
pip install Authlib
PyJWT + cryptography
โœ“ (RSA/EC)
โœ“
N/A
pip install PyJWT cryptography

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.

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

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.

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

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

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.

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

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.

Python
import jwt

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

Alat Terkait

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 SantosPeninjau teknis

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.