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:
- Payload: The claims dictionary containing user data
and reserved claims like
exp(expiration time) andiat(issued at). - Key: A secret string for symmetric algorithms (such as HMAC SHA-256) or a private key for asymmetric algorithms (such as RSA).
- 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:
- Signature Verification: PyJWT re-hashes the token's header and payload using the provided key and compares the result against the token's original signature. If any data was altered in transit, decoding fails immediately.
- Expiration (
exp): If anexpclaim is present, PyJWT compares it to the current UTC time. If the token is past its expiration, it raisesjwt.ExpiredSignatureError. - Not Before (
nbf): If present, PyJWT ensures the current time is after the specifiednbftimestamp. - Issuer (
iss) and Audience (aud): You can enforce issuer and audience checks by passingissuer="expected_issuer"oraudience="expected_audience"directly intojwt.decode().
decoded_payload = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"],
audience="my-api",
issuer="auth-service"
)Best Practices for PyJWT Authentication
- Do Not Store Sensitive Secrets in Payloads: JWT payloads are Base64URL-encoded, not encrypted. Anyone who intercepts the token can read the data unless the token is an encrypted JWT (JWE).
- Always Enforce the Algorithm: Never omit the
algorithmsparameter during decoding, as this prevents malicious actors from setting the algorithm header to"none". - Handle Errors Explicitly: Catch specific exceptions
such as
jwt.ExpiredSignatureErrorandjwt.InvalidTokenErrorto return clear HTTP status codes (such as401 Unauthorized) in authentication middleware.