JOSE implementation in Python providing JWT, JWS, JWE, and JWK functionality with multiple cryptographic backends.
75
High-level JSON Web Token functionality providing comprehensive support for encoding, decoding, and validating JWT tokens with automatic claim validation, flexible key handling, and extensive configuration options.
Creates JWT tokens from claims dictionaries with support for various signing algorithms and additional headers.
def encode(claims, key, algorithm='HS256', headers=None, access_token=None):
"""
Encodes a claims set and returns a JWT string.
Args:
claims (dict): A claims set to sign. Can include standard claims
(iss, sub, aud, exp, nbf, iat, jti) and custom claims
key (str or bytes or dict): The key to use for signing. Supports:
- String secrets for HMAC algorithms
- RSA/EC private keys in PEM format
- JWK dictionaries
algorithm (str): The algorithm to use for signing. Defaults to 'HS256'.
Supported algorithms: HS256, HS384, HS512, RS256, RS384, RS512,
ES256, ES384, ES512
headers (dict, optional): Additional headers to include in the JWT header.
Will override default headers with the same keys
access_token (str, optional): If present, the 'at_hash' claim will be
calculated and added to the claims
Returns:
str: The JWT token string in the format header.payload.signature
Raises:
JWTError: If there is an error encoding the claims
"""Usage Examples:
from jose import jwt
from jose.constants import ALGORITHMS
# Basic HMAC signing
token = jwt.encode({'user': 'john', 'role': 'admin'}, 'secret', algorithm=ALGORITHMS.HS256)
# With expiration time
import time
claims = {
'user': 'jane',
'exp': int(time.time()) + 3600, # Expires in 1 hour
'iat': int(time.time()), # Issued now
'iss': 'my-app' # Issuer
}
token = jwt.encode(claims, 'secret')
# RSA signing with private key
rsa_private_key = """-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
-----END PRIVATE KEY-----"""
token = jwt.encode({'user': 'alice'}, rsa_private_key, algorithm=ALGORITHMS.RS256)
# With additional headers
headers = {'kid': 'key-1', 'typ': 'JWT'}
token = jwt.encode({'user': 'bob'}, 'secret', headers=headers)
# With access token hash for OpenID Connect
access_token = 'SlAV32hkKG'
token = jwt.encode({'user': 'charlie'}, 'secret', access_token=access_token)Decodes and verifies JWT tokens with comprehensive claim validation and flexible verification options.
def decode(token, key, algorithms=None, options=None, audience=None, issuer=None, subject=None, access_token=None):
"""
Decodes and verifies a JWT token.
Args:
token (str): The JWT token to decode
key (str or bytes or dict or list): The verification key(s). Supports:
- String secrets for HMAC algorithms
- RSA/EC public keys in PEM format
- JWK dictionaries
- List of keys to try multiple keys
algorithms (str or list): Allowed algorithms for verification. If None,
raises an error. Use specific algorithms like ['HS256'] for security
options (dict, optional): Verification options controlling which claims
to verify and validation behavior
audience (str, optional): Expected audience claim ('aud'). Token must
contain this exact audience value
issuer (str or list or tuple, optional): Expected issuer claim(s) ('iss').
Token issuer must match one of the provided values
subject (str, optional): Expected subject claim ('sub'). Token must
contain this exact subject value
access_token (str, optional): Access token for 'at_hash' verification
in OpenID Connect scenarios
Returns:
dict: The decoded payload claims
Raises:
JWTError: If the token is invalid or verification fails
ExpiredSignatureError: If the token has expired
JWTClaimsError: If claim validation fails
"""Verification Options:
The options parameter accepts a dictionary with the following keys:
# Default options
options = {
'verify_signature': True, # Verify the token signature
'verify_exp': True, # Verify expiration time claim
'verify_nbf': True, # Verify not before time claim
'verify_iat': True, # Verify issued at time claim
'verify_aud': True, # Verify audience claim
'verify_iss': True, # Verify issuer claim
'verify_sub': True, # Verify subject claim
'verify_jti': True, # Verify JWT ID claim
'require_exp': False, # Require exp claim to be present
'require_iat': False, # Require iat claim to be present
'require_nbf': False, # Require nbf claim to be present
'require_aud': False, # Require aud claim to be present
'require_iss': False, # Require iss claim to be present
'require_sub': False, # Require sub claim to be present
'require_jti': False, # Require jti claim to be present
'leeway': 0 # Leeway in seconds for time-based claims
}Usage Examples:
from jose import jwt
from jose.exceptions import ExpiredSignatureError, JWTError
from datetime import timedelta
# Basic verification
try:
claims = jwt.decode(token, 'secret', algorithms=['HS256'])
print(f"User: {claims['user']}")
except JWTError as e:
print(f"Token validation failed: {e}")
# Verification with audience and issuer
claims = jwt.decode(
token,
'secret',
algorithms=['HS256'],
audience='my-app',
issuer='trusted-issuer'
)
# Verification with leeway for clock skew
options = {'leeway': 30} # 30 seconds leeway
claims = jwt.decode(token, 'secret', algorithms=['HS256'], options=options)
# Skip signature verification (for debugging only)
options = {'verify_signature': False}
claims = jwt.decode(token, 'secret', algorithms=['HS256'], options=options)
# Require specific claims
options = {
'require_exp': True,
'require_iss': True,
'require_aud': True
}
claims = jwt.decode(token, 'secret', algorithms=['HS256'], options=options)
# Multiple key verification (JWK set scenario)
keys = [
{'kty': 'oct', 'k': 'key1'},
{'kty': 'oct', 'k': 'key2'},
'backup-secret'
]
claims = jwt.decode(token, keys, algorithms=['HS256'])
# RSA public key verification
rsa_public_key = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----"""
claims = jwt.decode(token, rsa_public_key, algorithms=['RS256'])Retrieve JWT headers and claims without performing verification, useful for debugging and token inspection.
def get_unverified_header(token):
"""
Get JWT header without verification.
Args:
token (str): The JWT token
Returns:
dict: The JWT header dictionary
Raises:
JWTError: If the token format is invalid
"""
def get_unverified_headers(token):
"""
Get JWT header without verification (alias for get_unverified_header).
Args:
token (str): The JWT token
Returns:
dict: The JWT header dictionary
Raises:
JWTError: If the token format is invalid
"""
def get_unverified_claims(token):
"""
Get JWT payload claims without verification.
Args:
token (str): The JWT token
Returns:
dict: The JWT payload claims dictionary
Raises:
JWTError: If the token format is invalid
"""Usage Examples:
# Inspect token header (useful for getting 'kid' for key selection)
header = jwt.get_unverified_header(token)
print(f"Algorithm: {header['alg']}")
print(f"Key ID: {header.get('kid')}")
# Inspect token claims (useful for debugging expired tokens)
claims = jwt.get_unverified_claims(token)
print(f"Issuer: {claims.get('iss')}")
print(f"Subject: {claims.get('sub')}")
print(f"Expires: {claims.get('exp')}")
# Conditional verification based on header
header = jwt.get_unverified_header(token)
if header['alg'] == 'HS256':
claims = jwt.decode(token, hmac_secret, algorithms=['HS256'])
elif header['alg'] == 'RS256':
claims = jwt.decode(token, rsa_public_key, algorithms=['RS256'])The JWT specification defines several standard claims:
# Standard claims supported by python-jose
claims = {
'iss': 'issuer', # Issuer - who issued the token
'sub': 'subject', # Subject - who the token is about
'aud': 'audience', # Audience - who the token is intended for
'exp': 1234567890, # Expiration time (Unix timestamp)
'nbf': 1234567890, # Not before time (Unix timestamp)
'iat': 1234567890, # Issued at time (Unix timestamp)
'jti': 'unique-id', # JWT ID - unique identifier for the token
'at_hash': 'hash-value' # Access token hash (OpenID Connect)
}JWT operations can raise several specific exceptions:
class JWTError(JOSEError):
"""Base exception for JWT-related errors."""
class JWTClaimsError(JWTError):
"""JWT claims validation failed."""
class ExpiredSignatureError(JWTError):
"""JWT token has expired."""Common Error Scenarios:
from jose import jwt
from jose.exceptions import JWTError, ExpiredSignatureError, JWTClaimsError
try:
claims = jwt.decode(token, key, algorithms=['HS256'], audience='my-app')
except ExpiredSignatureError:
# Token has expired (exp claim)
print("Token has expired")
except JWTClaimsError as e:
# Claims validation failed (aud, iss, sub, etc.)
print(f"Claims validation failed: {e}")
except JWTError as e:
# Other JWT errors (invalid format, signature, etc.)
print(f"JWT error: {e}")The library provides built-in support for OpenID Connect JWT features:
# Access token hash calculation (at_hash claim)
access_token = 'SlAV32hkKG'
id_token = jwt.encode(
{'sub': '12345', 'aud': 'client-id'},
key,
access_token=access_token
)
# Verification includes at_hash validation
claims = jwt.decode(
id_token,
key,
algorithms=['HS256'],
access_token=access_token
)algorithms=None in productionaud, iss, and other relevant claimsexp times and handle ExpiredSignatureErrorleeway option to handle small time differences between systemskid header for key identificationInstall with Tessl CLI
npx tessl i tessl/pypi-python-jose