ToolDeck

JWT Decoder Python — راهنمای رمزگشایی JWT با PyJWT

·DevOps Engineer & Python Automation Specialist·بررسی‌شده توسطMaria Santos·منتشر شده

از رمزگشای JWT آنلاین رایگان مستقیم در مرورگرتان استفاده کنید — نیازی به نصب نیست.

امتحان کردن رمزگشای JWT آنلاین ←

هر API که از احراز هویت مبتنی بر توکن استفاده می‌کند، یک‌جایی یک JWT به دستتان می‌رسد، و دیر یا زود در توسعه لازم می‌شود محتوایش را بخوانید. یک رمزگشای JWT در Python آن رشته مبهم base64 را به یک دیکشنری ادعای خوانا تبدیل می‌کند که می‌توانید با آن کار کنید. بسته PyPI که نیاز دارید PyJWT است — با pip install PyJWT نصب می‌شود اما با import jwt وارد می‌شود. این راهنما jwt.decode() را با تأیید کامل امضا، رمزگشایی بدون کلید برای بازرسی سریع، رمزگشایی دستی با base64 بدون هیچ کتابخانه‌ای، تأیید کلید عمومی RS256، و اشتباهات رایجی که در سیستم‌های احراز هویت محیط تولید با آن‌ها مواجه شده‌ام را پوشش می‌دهد. برای بررسی سریع یک‌باره، رمزگشای آنلاین JWT این کار را فوری و بدون هیچ کدی انجام می‌دهد. تمام مثال‌ها برای Python 3.10+ و PyJWT نسخه ۲.x هستند.

  • با pip install PyJWT نصب کنید، سپس import jwt — نام بسته و نام وارد کردن متفاوتند، و این تقریباً همه را گیج می‌کند.
  • jwt.decode(token, key, algorithms=["HS256"]) یک دیکشنری ساده با ادعاها برمی‌گرداند. همیشه algorithms را صریح بدهید.
  • برای بازرسی ادعاها بدون تأیید: jwt.decode(token, options={"verify_signature": False}, algorithms=["HS256"]).
  • برای توکن‌های RSA/EC: pip install PyJWT cryptography — بسته cryptography برای الگوریتم‌های نامتقارن الزامی است.
  • رمزگشایی دستی (base64 + json) بدون هیچ کتابخانه‌ای کار می‌کند اما تمام اعتبارسنجی امضا و انقضا را نادیده می‌گیرد.

رمزگشایی JWT چیست؟

یک JSON Web Token سه بخش رمزگذاری‌شده با base64url است که با نقطه از هم جدا شده‌اند: یک هدر (الگوریتم و نوع توکن)، یک payload (ادعاها — شناسه کاربر، نقش‌ها، زمان انقضا)، و یک امضا. رمزگشایی JWT یعنی استخراج بخش‌های هدر و payload، رمزگشایی آن‌ها با base64url، و تجزیه JSON حاصل به یک دیکشنری از ادعاها.

هدر می‌گوید کدام الگوریتم برای امضای توکن استفاده شده و گاهی شامل kid (شناسه کلید) برای یافتن کلید تأیید مناسب است. payload داده‌های واقعی را نگه می‌دارد: توکن برای چه کسی صادر شده (sub)، کِی منقضی می‌شود (exp)، برای کدام سرویس در نظر گرفته شده (aud)، به علاوه هر ادعای سفارشی که برنامه شما تعریف می‌کند. بخش امضا ثابت می‌کند توکن دستکاری نشده، اما برای تأیید آن به کلید مشترک یا کلید عمومی نیاز دارید. رمزگشایی و تأیید دو عملیات جداگانه‌اند. می‌توانید payload را بدون تأیید امضا رمزگشایی کنید (مفید برای اشکال‌زدایی)، اما هرگز به ادعاهای تأییدنشده برای تصمیمات مجوزدهی اعتماد نکنید.

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

jwt.decode() — رمزگشایی و تأیید با PyJWT

jwt.decode() تابع اصلی از کتابخانه PyJWT است. توکن رمزگذاری‌شده، کلید مشترک (برای الگوریتم‌های HMAC) یا کلید عمومی (برای RSA/EC)، و یک لیست algorithms اجباری می‌گیرد. این تابع امضا را تأیید می‌کند، ادعاهای استاندارد مانند exp و nbf را بررسی می‌کند، و payload را به صورت یک دیکشنری Python برمی‌گرداند. اگر چیزی خطا داشته باشد — امضای نادرست، توکن منقضی‌شده، الگوریتم اشتباه — یک خطای مشخص می‌دهد.

مثال کارآمد حداقلی

Python 3.10+
import jwt

# یک کلید مشترک بین صادرکننده و این سرویس
SECRET_KEY = "k8s-webhook-signing-secret-2026"

# ابتدا یک توکن رمزگذاری کنید (شبیه‌سازی چیزی که سرور auth صادر می‌کند)
token = jwt.encode(
    {"sub": "usr_8f2a", "role": "admin", "team": "platform"},
    SECRET_KEY,
    algorithm="HS256"
)
print(token)
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3Jf...

# رمزگشایی و تأیید توکن
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
print(payload)
# {'sub': 'usr_8f2a', 'role': 'admin', 'team': 'platform'}
print(payload["role"])
# admin

پارامتر algorithms یک لیست است، نه یک رشته تنها، و در PyJWT 2.x اجباری است. این یک ویژگی امنیتی است: بدون آن، مهاجم می‌تواند توکنی با alg: none در هدر بسازد و تأیید را کاملاً دور بزند. همیشه دقیقاً مشخص کنید برنامه شما کدام الگوریتم‌ها را می‌پذیرد. اگر فقط توکن‌های HS256 صادر می‌کنید، لیست باید ["HS256"] باشد — نه ["HS256", "RS256", "none"]. کوچک نگه داشتن لیست سطح حمله را کاهش می‌دهد.

چیزی که در ابتدا گیجم کرد: PyJWT 2.x تغییر داد jwt.encode() تا رشته برگرداند نه bytes. اگر پاسخ‌های قدیمی Stack Overflow را می‌خوانید که .decode("utf-8") روی توکن رمزگذاری‌شده فراخوانی می‌کنند، آن کد متعلق به دوره PyJWT 1.x است و روی نسخه 2.x یک AttributeError می‌دهد. توکن از قبل یک رشته است — فقط مستقیم از آن استفاده کنید.

چرخه کامل با انقضا

Python 3.10+ — رمزگذاری سپس رمزگشایی با exp
import jwt
from datetime import datetime, timedelta, timezone

SECRET_KEY = "webhook-processor-secret"

# یک توکن که در ۱ ساعت منقضی می‌شود
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")

# بعداً، وقتی توکن در هدر درخواست می‌رسد:
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")
توجه:PyJWT اشیاء datetime را هنگام رمزگذاری به صورت خودکار به مُهر زمانی Unix تبدیل می‌کند. هنگام رمزگشایی، ادعاهای exp، iat و nbf به صورت عدد صحیح برمی‌گردند، نه اشیاء datetime. باید خودتان آن‌ها را با datetime.fromtimestamp(payload["exp"], tz=timezone.utc) تبدیل کنید.

رمزگشایی JWT بدون تأیید امضا

گاهی باید ادعاها را پیش از تأیید توکن بخوانید. یک سناریوی رایج: هدر توکن شامل یک فیلد kid (شناسه کلید) است و باید کلید عمومی مطابق را از یک endpoint JWKS دریافت کنید پیش از اینکه بتوانید تأیید کنید. PyJWT این را با گزینه verify_signature: False پشتیبانی می‌کند. همچنان لیست algorithms را می‌دهید، اما آرگومان key نادیده گرفته می‌شود.

Python 3.10+ — رمزگشایی بدون تأیید
import jwt

token = (
    "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNpZy0xNzI2In0"
    ".eyJzdWIiOiJ1c3JfM2M3ZiIsInNjb3BlIjoicmVhZDpvcmRlcnMiLCJpc3MiOiJhdXRoLmV4YW1wbGUuY29tIn0"
    ".signature_placeholder"
)

# مرحله ۱: خواندن ادعاها بدون تأیید برای دریافت اطلاعات مسیریابی
unverified = jwt.decode(
    token,
    options={"verify_signature": False},
    algorithms=["RS256"]
)
print(unverified)
# {'sub': 'usr_3c7f', 'scope': 'read:orders', 'iss': 'auth.example.com'}

# مرحله ۲: خواندن هدر برای یافتن کلید مناسب
header = jwt.get_unverified_header(token)
print(header)
# {'alg': 'RS256', 'typ': 'JWT', 'kid': 'sig-1726'}
# حالا از header['kid'] برای دریافت کلید عمومی صحیح از endpoint JWKS خود استفاده کنید
هشدار:توکن‌های تأییدنشده قابل اعتماد نیستند. از این الگو فقط برای تصمیمات مسیریابی (کدام کلید را دریافت کنیم، کدام tenant را جستجو کنیم) استفاده کنید. هرگز بر اساس ادعاهای تأییدنشده تصمیمات مجوزدهی نگیرید. مهاجم می‌تواند هر چیزی در payload قرار دهد.

یک نکته ظریف اینجا هست. jwt.get_unverified_header() فقط هدر — بخش اول — را می‌خواند. فراخوانی jwt.decode() با verify_signature: False payload (بخش دوم) را می‌خواند. با هر دو، می‌توانید همه چیز را از یک توکن بدون کلید استخراج کنید. PyJWT همچنان اعتبارسنجی می‌کند که توکن ساختار درستی دارد (سه بخش جداشده با نقطه، base64 معتبر، JSON معتبر) حتی وقتی تأیید امضا خاموش است. اگر ساختار توکن نادرست باشد، DecodeError صادر می‌کند صرف‌نظر از گزینه‌هایی که می‌دهید.

مرجع پارامترهای jwt.decode()

امضای کامل jwt.decode(jwt, key, algorithms, options, audience, issuer, leeway, require) است. تمام پارامترها بعد از algorithms فقط کلیدواژه‌ای هستند.

پارامتر
نوع
پیش‌فرض
توضیح
jwt
str | bytes
(اجباری)
رشته JWT رمزگذاری‌شده برای رمزگشایی
key
str | bytes | dict
(اجباری)
کلید مشترک (HMAC) یا کلید عمومی (RSA/EC) برای تأیید امضا
algorithms
list[str]
(اجباری)
الگوریتم‌های مجاز — مثلاً ["HS256"]، ["RS256"]. هرگز این پارامتر را حذف نکنید.
options
dict
{}
تنظیم پرچم‌های اعتبارسنجی: verify_signature، verify_exp، verify_aud و غیره
audience
str | list[str]
None
مقدار مورد انتظار ادعای aud — در صورت عدم تطابق InvalidAudienceError می‌دهد
issuer
str
None
مقدار مورد انتظار ادعای iss — در صورت عدم تطابق InvalidIssuerError می‌دهد
leeway
timedelta | int
0
ثانیه‌های تحمل اختلاف ساعت برای بررسی‌های exp و nbf
require
list[str]
[]
ادعاهایی که باید حضور داشته باشند — در غیر این صورت MissingRequiredClaimError می‌دهد

دیکشنری options کنترل دقیقی روی اینکه PyJWT کدام اعتبارسنجی‌ها را انجام دهد می‌دهد. کلیدها به بررسی‌های جداگانه نگاشت می‌شوند: verify_signature، verify_exp، verify_nbf، verify_iss، verify_aud و verify_iat. همه پیش‌فرض True هستند مگر اینکه صریحاً آن‌ها را False کنید. در محیط تولید، همه این‌ها را با مقادیر پیش‌فرض بگذارید. تنها موقعی که بررسی‌های جداگانه را غیرفعال می‌کنم در طول توسعه است، وقتی با توکن‌های آزمایشی قدیمی کار می‌کنم و نیاز دارم به صورت موقت انقضا را نادیده بگیرم.

Python 3.10+ — استفاده از options و require
import jwt

# الزام حضور ادعاهای مشخص — در صورت غیاب MissingRequiredClaimError می‌دهد
payload = jwt.decode(
    token,
    SECRET_KEY,
    algorithms=["HS256"],
    options={"require": ["exp", "iss", "sub"]},
    issuer="auth.internal.example.com",
)

# فقط در توسعه: نادیده گرفتن انقضا برای آزمایش با توکن‌های قدیمی
dev_payload = jwt.decode(
    token,
    SECRET_KEY,
    algorithms=["HS256"],
    options={"verify_exp": False},  # در محیط تولید استفاده نکنید
)

رمزگشایی دستی JWT با base64 و json

می‌توانید payload یک JWT را با استفاده از کتابخانه استاندارد Python رمزگشایی کنید — بدون pip install. این چند جا واقعاً به کارتان می‌آید: اسکریپت‌های اشکال‌زدایی که افزودن یک وابستگی بیش از حد است، محیط‌های CI محدود که فقط کتابخانه استاندارد در دسترس است، توابع AWS Lambda که می‌خواهید زمان راه‌اندازی سرد را کمینه کنید، یا صرفاً برای درک اینکه JWT در واقع چیست. فرآیند ساده است: روی نقطه‌ها تقسیم کنید، بخشی که می‌خواهید را بگیرید، padding base64 اضافه کنید، رمزگشایی کنید، و JSON را تجزیه کنید.

ماژول‌های base64 و json هر دو در کتابخانه استاندارد Python هستند، بنابراین این روش روی هر نصب Python از نسخه ۳.۶ به بعد کار می‌کند. توابع زیر هدر (بخش اول) و payload (بخش دوم) را جداگانه مدیریت می‌کنند:

Python 3.10+ — رمزگشایی دستی JWT بدون هیچ کتابخانه‌ای
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

ترفند padding (+= "=" * (-len(s) % 4)) همان چیزی است که همه فراموش می‌کنند. base64url در JWT کاراکترهای انتهایی = را حذف می‌کند، اما urlsafe_b64decode در Python به آن‌ها نیاز دارد. بدون این اصلاح padding، خطای binascii.Error: Incorrect padding می‌گیرید.

توجه:رمزگشایی دستی امضا را تأیید نمی‌کند. هر کسی می‌تواند payload یک JWT را تغییر داده و دوباره رمزگذاری کند. از این روش فقط برای بازرسی و اشکال‌زدایی استفاده کنید، نه برای منطق مجوزدهی. برای هر کاری که اهمیت دارد، از jwt.decode() با یک کلید واقعی استفاده کنید.

رمزگشایی JWT از پاسخ‌های API و فایل‌های توکن

دو سناریوی رایج در دنیای واقعی: استخراج JWT از یک پاسخ HTTP (یک endpoint توکن OAuth، یک API ورود)، و خواندن توکن‌ها از فایل‌ها (اعتبارنامه‌های حساب سرویس، اسرار مانت‌شده Kubernetes، توکن‌های ذخیره‌شده روی دیسک). هر دو نیاز به مدیریت خطای مناسب دارند. درخواست‌های شبکه شکست می‌خورند. فایل‌ها ممکن است وجود نداشته باشند. توکن‌ها بین زمان ذخیره‌شدن و زمان خوانده‌شدن منقضی می‌شوند.

مثال‌های زیر از httpx برای فراخوانی‌های HTTP استفاده می‌کنند (اگر ترجیح می‌دهید می‌توانید requests را جایگزین کنید، الگو یکسان است) و از pathlib.Path برای عملیات فایل. هر مثال خطاهای مشخص PyJWT را مدیریت می‌کند نه یک except Exception کلی، تا بتوانید به هر حالت خطا به درستی پاسخ دهید: احراز هویت مجدد در صورت انقضا، هشدار در صورت خرابی امضا، تلاش مجدد در صورت timeout شبکه.

رمزگشایی JWT از یک پاسخ API

Python 3.10+ — رمزگشایی JWT از endpoint توکن OAuth
import jwt
import httpx  # or requests

TOKEN_ENDPOINT = "https://auth.example.com/oauth/token"
SECRET_KEY = "shared-webhook-signing-key"

def get_and_decode_token() -> dict:
    """Fetch an access token from the auth server and decode it."""
    try:
        response = httpx.post(
            TOKEN_ENDPOINT,
            data={
                "grant_type": "client_credentials",
                "client_id": "svc_order_processor",
                "client_secret": "cs_9f3a7b2e",
            },
            timeout=10.0,
        )
        response.raise_for_status()
    except httpx.HTTPError as exc:
        raise RuntimeError(f"Token request failed: {exc}") from exc

    token_data = response.json()
    access_token = token_data["access_token"]

    try:
        payload = jwt.decode(
            access_token,
            SECRET_KEY,
            algorithms=["HS256"],
            audience="https://api.example.com",
        )
        return payload
    except jwt.InvalidTokenError as exc:
        raise RuntimeError(f"Invalid token from auth server: {exc}") from exc


claims = get_and_decode_token()
print(f"Service: {claims['sub']}, Scopes: {claims.get('scope', 'none')}")

رمزگشایی JWT از یک فایل

Python 3.10+ — خواندن و رمزگشایی یک فایل توکن ذخیره‌شده
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 از خط فرمان

گاهی فقط باید بدون نوشتن اسکریپت از ترمینال نگاهی به یک توکن بیندازید. شاید دارید یک جریان OAuth اشکال‌زدایی می‌کنید و می‌خواهید ببینید چه چیزی در هدر Authorization است، یا یک توکن از DevTools مرورگر گرفتید و می‌خواهید انقضای آن را بررسی کنید. پرچم -c در Python این کار را در یک خط ممکن می‌کند. توکن را pipe کنید و ادعاها را به صورت JSON قالب‌بندی‌شده دریافت کنید. نه اسکریپت جداگانه‌ای لازم است، نه محیط مجازی.

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"

برای یک گزینه بصری بدون هیچ راه‌اندازی ترمینال، توکن خود را در رمزگشای JWT ToolDeck وارد کنید تا هدر، payload و وضعیت تأیید امضا را فوری ببینید.

python-jose و سایر جایگزین‌ها

python-jose یک کتابخانه JWT جایگزین است که به صورت بومی از JWS، JWE (توکن‌های رمزشده) و JWK پشتیبانی می‌کند. اگر برنامه شما نیاز به مدیریت JWT های رمزشده (JWE) دارد — جایی که payload خود رمز شده، نه فقط امضا شده — python-jose گزینه مناسب است چون PyJWT اصلاً از JWE پشتیبانی نمی‌کند. این کتابخانه همچنین مدیریت مجموعه کلید JWKS داخلی دارد، که یکپارچه‌سازی با ارائه‌دهندگان هویت مثل Auth0، Okta یا Keycloak که مجموعه کلیدهای چرخشی را نمایان می‌کنند را ساده‌تر می‌کند. رابط رمزگشایی تقریباً یکسان با PyJWT است، بنابراین تغییر بین آن‌ها نیاز به تغییرات کمی در کد دارد:

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

توصیه من: با PyJWT شروع کنید. ۹۵٪ موارد استفاده JWT را پوشش می‌دهد، بزرگ‌ترین جامعه را دارد، و API آن تمیز است. اگر به پشتیبانی JWE نیاز دارید یا مدیریت JWKS آن را ترجیح می‌دهید، به python-jose تغییر دهید. گزینه سومی که ارزش اشاره دارد Authlib است، که مدیریت JWT را درون یک چارچوب OAuth/OIDC بزرگ‌تر گنجانده. اگر از قبل از Authlib برای جریان‌های OAuth client استفاده می‌کنید، ماژول authlib.jose.jwt آن شما را از افزودن یک وابستگی JWT دوم بی‌نیاز می‌کند. در غیر این صورت، یک وابستگی سنگین فقط برای رمزگشایی توکن است.

خروجی ترمینال با رنگ‌آمیزی نحوی

خواندن ادعاهای خام JWT در ترمینال برای یک نگاه سریع کافی است، اما اگر مدام payload توکن‌ها را دیباگ می‌کنید (من این کار را روزانه هنگام ساخت یک دروازه احراز هویت داخلی انجام می‌دادم)، خروجی رنگی تفاوت واقعی ایجاد می‌کند. مقادیر رشته‌ای، اعداد، بولین‌ها و null همه با رنگ‌های متمایز نمایش داده می‌شوند، به این معنی که می‌توانید یک مجوز گمشده یا مُهر زمانی انقضای اشتباه را بدون خواندن هر کاراکتر تشخیص دهید.

کتابخانه rich (pip install rich) یک تابع print_json دارد که یک رشته JSON یا یک دیکشنری Python می‌گیرد و آن را با رنگ‌آمیزی نحوی کامل در ترمینال چاپ می‌کند. آن را با PyJWT ترکیب کنید برای یک جریان کار دو خطی بازرسی JWT:

Python 3.10+ — خروجی ترمینال با rich
# 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 شامل کدهای فرار ANSI است. آن را در فایل‌ها ننویسید یا از endpoint های API برنگردانید — فقط برای نمایش در ترمینال است. وقتی به خروجی متن ساده نیاز دارید از json.dumps() استفاده کنید.

کار با دسته‌های بزرگ توکن

توکن‌های JWT خودشان کوچک هستند (معمولاً زیر ۲ کیلوبایت)، اما سناریوهایی وجود دارد که آن‌ها را به صورت انبوه پردازش می‌کنید. تحلیل لاگ حسابرسی پس از یک رویداد امنیتی. اسکریپت‌های مهاجرت نشست هنگام تغییر ارائه‌دهندگان auth. اعتبارسنجی دسته‌ای انطباق که باید ثابت کنید هر توکن صادرشده در ۹۰ روز گذشته با کلید صحیح امضا شده. اگر ده‌ها هزار توکن در یک فایل لاگ NDJSON دارید، پردازش خط به خط از بارگذاری کل فایل در حافظه جلوگیری می‌کند و به شما اجازه می‌دهد نتایج را به صورت تدریجی گزارش کنید.

اعتبارسنجی دسته‌ای توکن‌ها از یک لاگ حسابرسی

Python 3.10+ — اعتبارسنجی جریانی توکن
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

استخراج ادعاها از یک خروجی توکن NDJSON

Python 3.10+ — استخراج و تبدیل ادعاها از لاگ توکن
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")
توجه:برای فایل‌های زیر چند صد مگابایت، خواندن خط به خط به اندازه کافی کارآمد است. اگر با محدودیت عملکرد روی dump های توکن بسیار بزرگ مواجه شدید، از multiprocessing.Pool برای توزیع اعتبارسنجی روی هسته‌ها استفاده کنید، چون هر توکن مستقل است.

اشتباهات رایج

این چهار اشتباه به طور مکرر در بررسی کد و سوالات Stack Overflow دیده می‌شوند. هر کدام به راحتی رخ می‌دهند، و پیام خطایی که PyJWT می‌دهد همیشه مستقیم به علت اصلی اشاره نمی‌کند. من دیده‌ام که مشکل نام‌گذاری بسته به تنهایی ساعت‌ها زمان اشکال‌زدایی را هدر داده وقتی کسی کتابخانه اشتباه را نصب کرده و یک API کاملاً غیرمنتظره دریافت کرده.

اشتباه گرفتن نام‌های بسته PyJWT و python-jwt

مشکل: اجرای pip install jwt یا pip install python-jwt یک بسته کاملاً متفاوت نصب می‌کند. import jwt سپس خطا می‌دهد یا یک API ناآشنا می‌دهد.

راه‌حل: همیشه با pip install PyJWT نصب کنید. وارد کردن با import jwt است. با pip show PyJWT بررسی کنید که بسته درست نصب شده.

After · Python
Before · Python
pip install PyJWT

import jwt  # correct — this is PyJWT
print(jwt.__version__)  # 2.x
pip install jwt
# or
pip install python-jwt

import jwt  # wrong package — different API entirely
حذف پارامتر algorithms

مشکل: در PyJWT 1.x، algorithms اختیاری بود و به صورت پیش‌فرض هر الگوریتمی را می‌پذیرفت. این یک آسیب‌پذیری امنیتی ایجاد می‌کرد که مهاجم می‌توانست alg: none تنظیم کند. PyJWT 2.x اکنون DecodeError می‌دهد اگر algorithms نباشد.

راه‌حل: همیشه algorithms را به عنوان یک لیست صریح بدهید. فقط الگوریتم‌هایی را که برنامه شما واقعاً توکن با آن‌ها صادر می‌کند استفاده کنید.

After · Python
Before · Python
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
# PyJWT 2.x — this raises DecodeError
payload = jwt.decode(token, SECRET_KEY)
# DecodeError: algorithms must be specified
استفاده از jwt.decode() با نوع کلید اشتباه برای RS256

مشکل: دادن یک کلید رشته‌ای به jwt.decode() با algorithms=["RS256"] باعث InvalidSignatureError می‌شود. RS256 نیاز به یک کلید عمومی PEM دارد، نه یک رشته کلید مشترک.

راه‌حل: کلید عمومی PEM را از یک فایل یا متغیر محیطی بارگذاری کنید. بسته cryptography را نصب کنید: pip install PyJWT cryptography.

After · Python
Before · Python
public_key = open("public_key.pem").read()
payload = jwt.decode(token, public_key, algorithms=["RS256"])
# This fails — RS256 needs a public key, not a string secret
payload = jwt.decode(token, "my-secret", algorithms=["RS256"])
# InvalidSignatureError
فراموش کردن padding در رمزگشایی دستی

مشکل: رمزگذاری base64url در JWT کاراکترهای انتهایی = را حذف می‌کند. base64.urlsafe_b64decode در Python خطای binascii.Error: Incorrect padding می‌دهد اگر بخش خام را بدون اصلاح padding بدهید.

راه‌حل: پیش از رمزگشایی padding اضافه کنید: segment += '=' * (-len(segment) % 4). این فرمول همیشه تعداد درست کاراکترهای padding (۰، ۱، ۲ یا ۳) تولید می‌کند.

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

PyJWT در برابر جایگزین‌ها — مقایسه سریع

روش
تأیید امضا
اعتبارسنجی ادعاها
نوع‌های سفارشی
نیاز به نصب
PyJWT jwt.decode()
✓ (exp, aud, iss, nbf)
ندارد (dict برمی‌گرداند)
pip install PyJWT
PyJWT بدون تأیید
ندارد
pip install PyJWT
رمزگشایی دستی با base64
ندارد
خیر (کتابخانه استاندارد)
python-jose jwt.decode()
ندارد
pip install python-jose
Authlib jwt.decode()
ندارد
pip install Authlib
PyJWT + cryptography
✓ (RSA/EC)
ندارد
pip install PyJWT cryptography

PyJWT نقطه شروع مناسب برای اکثر برنامه‌های Python است. HMAC و (با backend cryptography) تأیید امضای RSA و EC را پوشش می‌دهد. اگر به JWE (توکن‌های رمزشده) نیاز دارید، به python-jose یا Authlib تغییر دهید. رمزگشایی دستی base64 برای اشکال‌زدایی کار می‌کند اما هیچ تضمین ایمنی نمی‌دهد.

اینجاست که من از هر گزینه استفاده می‌کنم: PyJWT برای هر سرویس وب استاندارد که تأیید HS256 یا RS256 انجام می‌دهد. python-jose وقتی معماری شامل توکن‌های رمزشده یا چرخش JWKS است. base64 دستی برای بازرسی سریع در محیط‌هایی که pip در دسترس نیست (کانتینرهای CI، هاست‌های تولید محدود، راه‌اندازی‌های سرد AWS Lambda که می‌خواهید وابستگی‌ها را کمینه کنید). Authlib وقتی پروژه از قبل از آن برای جریان‌های OAuth client استفاده می‌کند و افزودن کتابخانه JWT دیگری اضافی است.

برای یک جایگزین بدون کد، هر توکنی را در رمزگشای JWT وارد کنید تا هدر و payload رمزگشایی‌شده را با بازخورد اعتبارسنجی ادعا ببینید.

سوالات متداول

چگونه بدون تأیید امضا یک JWT را در Python رمزگشایی کنم؟

مقدار options={"verify_signature": False} و algorithms=["HS256"] را به jwt.decode() بدهید. این کار دیکشنری payload را بدون بررسی امضا برمی‌گرداند. این روش را فقط برای بازرسی استفاده کنید — خواندن ادعاها پیش از دریافت کلید عمومی مناسب، یا اشکال‌زدایی در محیط توسعه. هرگز تأیید امضا را برای توکن‌هایی که دسترسی به منابع مهم را کنترل می‌کنند نادیده نگیرید.

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

تفاوت بین PyJWT و python-jwt چیست؟

PyJWT (نصب با pip install PyJWT، وارد کردن با import jwt) محبوب‌ترین کتابخانه JWT در Python با بیش از ۸۰ میلیون بارگیری ماهانه است. python-jwt (نصب با pip install python_jwt) یک کتابخانه کاملاً جداگانه و بسیار کم‌استفاده‌تر با رابط برنامه‌نویسی متفاوت است. اگر در کد کسی import jwt می‌بینید، از PyJWT استفاده می‌کند. این سردرگمی از تفاوت نام بسته در PyPI (PyJWT) با نام وارد کردن (jwt) ناشی می‌شود. تا زمانی که دلیل خاصی ندارید، از PyJWT استفاده کنید.

چگونه یک JWT با RS256 را در Python رمزگشایی کنم؟

هر دو PyJWT و بسته cryptography را نصب کنید: pip install PyJWT cryptography. سپس کلید عمومی رمزگذاری‌شده با PEM را به عنوان آرگومان key و algorithms=["RS256"] را بدهید. PyJWT تأیید امضای RSA را به کتابخانه cryptography واگذار می‌کند. بدون نصب بسته cryptography، PyJWT هنگام استفاده از الگوریتم‌های RSA یا EC خطا می‌دهد.

Python
import jwt

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

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

چرا PyJWT خطای ExpiredSignatureError می‌دهد؟

PyJWT به صورت پیش‌فرض ادعای exp (انقضا) را بررسی می‌کند. اگر زمان UTC جاری از مُهر زمانی exp گذشته باشد، خطای jwt.ExpiredSignatureError صادر می‌کند. می‌توانید با پارامتر leeway تحمل اختلاف ساعت اضافه کنید: jwt.decode(token, key, algorithms=["HS256"], leeway=timedelta(seconds=30)). این کار یک دوره لطف ۳۰ ثانیه‌ای می‌دهد. برای غیرفعال کردن کامل بررسی انقضا (که برای محیط تولید توصیه نمی‌شود)، 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")

آیا می‌توانم بدون هیچ کتابخانه‌ای ادعاهای JWT را در Python بخوانم؟

بله. توکن را روی نقطه‌ها تقسیم کنید، بخش دوم (payload) را بگیرید، با کاراکتر = آن را تا مضربی از ۴ پر کنید، سپس آن را با base64url رمزگشایی کرده و JSON حاصل را تجزیه کنید. این روش دیکشنری ادعاها را می‌دهد اما امضا را تأیید نمی‌کند. در محیط‌های محدود که نمی‌توانید PyJWT نصب کنید یا برای اسکریپت‌های اشکال‌زدایی سریع مفید است.

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

چگونه ادعای audience را با PyJWT اعتبارسنجی کنم؟

پارامتر audience را به jwt.decode() بدهید: jwt.decode(token, key, algorithms=["HS256"], audience="https://api.example.com"). PyJWT ادعای aud در توکن را با مقداری که شما می‌دهید مقایسه می‌کند. اگر توکن ادعای aud نداشته باشد یا مقدار مطابقت نداشته باشد، خطای jwt.InvalidAudienceError می‌دهد. همچنین می‌توانید لیستی از audience‌های قابل قبول بدهید اگر سرویس شما توکن‌های مخصوص چند API را می‌پذیرد.

Python
import jwt

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

ابزارهای مرتبط

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 Santosبازبین فنی

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.