How to Use PyJWT to Encode, Decode, and Validate JWTs

JSON Web Tokens (JWTs) provide a stateless, compact mechanism for securely transmitting information between parties as a JSON object. This article demonstrates how to use Python's PyJWT library to manage the complete lifecycle of authentication tokens. You will learn how to encode data payloads into signed tokens, decode them back into readable Python dictionaries, and automatically validate cryptographic signatures and standard claims such as expiration dates.

Installing PyJWT

To begin working with JWTs in Python, install the PyJWT package using pip. If you plan to use asymmetric cryptographic algorithms like RS256, install it with the cryptography dependency:

pip install pyjwt
# Or for asymmetric algorithm support:
pip install "pyjwt[crypto]"

Encoding a JSON Web Token

Encoding converts a Python dictionary (the payload) into a signed token string. The payload typically contains user identifiers and standard JWT claims.

The jwt.encode() function takes three primary arguments:

  1. Payload: The claims dictionary containing user data and reserved claims like exp (expiration time) and iat (issued at).
  2. Key: A secret string for symmetric algorithms (such as HMAC SHA-256) or a private key for asymmetric algorithms (such as RSA).
  3. Algorithm: The hashing algorithm to sign the token (e.g., "HS256").
import datetime
import jwt

# Secret key used for signing symmetric tokens
SECRET_KEY = "your-super-secret-key"

# Define the payload
payload = {
    "user_id": 42,
    "username": "johndoe",
    "iat": datetime.datetime.now(datetime.timezone.utc),
    "exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=1)
}

# Encode the token
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
print("Encoded Token:", token)

In PyJWT version 2.0 and later, jwt.encode() returns a standard str rather than raw bytes.

Decoding and Validating Tokens

The jwt.decode() method simultaneously unpacks the payload, verifies the cryptographic signature, and checks standard registered claims.

To prevent algorithm downgrade attacks, PyJWT requires you to explicitly pass an algorithms list containing the allowed hashing methods:

try:
    decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
    print("Decoded Payload:", decoded_payload)
except jwt.ExpiredSignatureError:
    print("The token has expired.")
except jwt.InvalidTokenError:
    print("The token signature is invalid or malformed.")

How PyJWT Validates Claims

When jwt.decode() runs, it performs automatic security checks:

decoded_payload = jwt.decode(
    token,
    SECRET_KEY,
    algorithms=["HS256"],
    audience="my-api",
    issuer="auth-service"
)

Best Practices for PyJWT Authentication