JSON Web Token implementation in Python with support for JWT, JWS, JWK, and JWKS
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Cryptographic algorithm registration and management system providing custom algorithm support, algorithm whitelisting for security, and flexible algorithm configuration for both global and instance-level operations.
Global functions for managing algorithms across all PyJWT operations using a shared algorithm registry.
def register_algorithm(alg_id: str, alg_obj) -> None:
"""
Register a custom algorithm globally for all JWT/JWS operations.
Args:
alg_id (str): Algorithm identifier (e.g., 'CUSTOM256')
alg_obj: Algorithm implementation object
Raises:
ValueError: Algorithm already registered
TypeError: Invalid algorithm object
"""
def unregister_algorithm(alg_id: str) -> None:
"""
Remove an algorithm from the global registry.
Args:
alg_id (str): Algorithm identifier to remove
Raises:
KeyError: Algorithm not registered
"""
def get_algorithm_by_name(alg_name: str):
"""
Retrieve algorithm implementation by name from global registry.
Args:
alg_name (str): Algorithm name
Returns:
Algorithm object implementation
Raises:
NotImplementedError: Algorithm not supported or cryptography missing
"""
def get_unverified_header(jwt: str | bytes) -> dict:
"""
Extract JWT header without signature verification.
Args:
jwt (str | bytes): JWT token
Returns:
dict: JWT header dictionary
Raises:
DecodeError: Invalid token format
InvalidTokenError: Invalid header structure
"""Usage examples:
import jwt
from jwt.algorithms import Algorithm
# Check algorithm availability
try:
alg = jwt.get_algorithm_by_name('RS256')
print(f"RS256 available: {alg}")
except NotImplementedError as e:
print(f"RS256 not available: {e}")
# Extract header for algorithm info
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..."
header = jwt.get_unverified_header(token)
print(f"Token algorithm: {header['alg']}")
# Register custom algorithm globally
class CustomHMAC(Algorithm):
def sign(self, msg, key):
# Custom signing implementation
import hashlib
import hmac
return hmac.new(key, msg, hashlib.sha3_256).digest()
def verify(self, msg, key, sig):
# Custom verification implementation
expected = self.sign(msg, key)
return hmac.compare_digest(sig, expected)
jwt.register_algorithm('CUSTOM-HMAC', CustomHMAC())
# Use custom algorithm
payload = {'user_id': 123}
token = jwt.encode(payload, b'secret', algorithm='CUSTOM-HMAC')
decoded = jwt.decode(token, b'secret', algorithms=['CUSTOM-HMAC'])
# Remove algorithm when no longer needed
jwt.unregister_algorithm('CUSTOM-HMAC')Algorithm management for specific PyJWT and PyJWS instances with custom algorithm sets and whitelisting.
# PyJWS instance methods
class PyJWS:
def register_algorithm(self, alg_id: str, alg_obj) -> None:
"""Register algorithm for this JWS instance only."""
def unregister_algorithm(self, alg_id: str) -> None:
"""Unregister algorithm from this JWS instance."""
def get_algorithm_by_name(self, alg_name: str):
"""Get algorithm from this JWS instance."""
def get_algorithms(self) -> list:
"""List algorithms available to this JWS instance."""
# PyJWT inherits algorithm management through its internal PyJWS instanceUsage examples:
import jwt
# Create JWS instance with limited algorithms
jws = jwt.PyJWS(algorithms=['HS256', 'HS512']) # Only allow HMAC
print(f"Allowed algorithms: {jws.get_algorithms()}")
# Add algorithm to specific instance
custom_alg = CustomHMAC()
jws.register_algorithm('CUSTOM', custom_alg)
# This instance now supports CUSTOM, but global instance doesn't
token = jws.encode(b'payload', b'key', algorithm='CUSTOM')
# Different instance with different algorithms
secure_jws = jwt.PyJWS(algorithms=['RS256', 'ES256']) # Only asymmetric
secure_jws.register_algorithm('CUSTOM-RSA', CustomRSA())Security-focused algorithm management with explicit whitelisting and algorithm restrictions.
import jwt
# Secure algorithm whitelisting
ALLOWED_ALGORITHMS = ['RS256', 'ES256', 'EdDSA'] # Only strong asymmetric
def verify_secure_token(token, key):
"""Verify token with strict algorithm policy."""
return jwt.decode(token, key, algorithms=ALLOWED_ALGORITHMS)
# Instance with security policy
secure_jwt = jwt.PyJWT()
secure_jws = jwt.PyJWS(algorithms=ALLOWED_ALGORITHMS)
# Block weak algorithms
weak_algorithms = ['none', 'HS256'] # Example: block 'none' and symmetric
for alg in weak_algorithms:
try:
jwt.unregister_algorithm(alg)
except KeyError:
pass # Algorithm not registered
# Verify no weak algorithms remain
available = jwt.PyJWS().get_algorithms()
dangerous = set(available) & set(weak_algorithms)
if dangerous:
print(f"Warning: Dangerous algorithms still available: {dangerous}")Base interface for implementing custom algorithms compatible with PyJWT.
class Algorithm:
"""
Base class for all algorithm implementations.
Custom algorithms must inherit from this class and implement
required methods for signing and verification.
"""
def sign(self, msg: bytes, key) -> bytes:
"""
Sign message with the given key.
Args:
msg (bytes): Message to sign
key: Cryptographic key for signing
Returns:
bytes: Signature
"""
raise NotImplementedError()
def verify(self, msg: bytes, key, sig: bytes) -> bool:
"""
Verify signature against message and key.
Args:
msg (bytes): Original message
key: Cryptographic key for verification
sig (bytes): Signature to verify
Returns:
bool: True if signature is valid
"""
raise NotImplementedError()
def prepare_key(self, key):
"""
Prepare and validate key for use with this algorithm.
Args:
key: Raw key material
Returns:
Prepared key object
Raises:
InvalidKeyError: Key is invalid for this algorithm
"""
return keyExamples of implementing custom algorithms for specific use cases:
import hashlib
import hmac
from jwt.algorithms import Algorithm
class HMACSHA3_256(Algorithm):
"""HMAC using SHA3-256 hash function."""
def sign(self, msg: bytes, key: bytes) -> bytes:
return hmac.new(key, msg, hashlib.sha3_256).digest()
def verify(self, msg: bytes, key: bytes, sig: bytes) -> bool:
expected = self.sign(msg, key)
return hmac.compare_digest(sig, expected)
def prepare_key(self, key):
if not isinstance(key, (bytes, str)):
raise jwt.InvalidKeyError("HMAC key must be bytes or string")
if isinstance(key, str):
key = key.encode('utf-8')
return key
# Register and use
jwt.register_algorithm('HS3-256', HMACSHA3_256())
token = jwt.encode({'data': 'test'}, 'secret', algorithm='HS3-256')from jwt.algorithms import Algorithm
from jwt.exceptions import InvalidKeyError
class SecureHMAC(Algorithm):
"""HMAC with minimum key length requirement."""
MIN_KEY_LENGTH = 32 # Require 256-bit keys minimum
def prepare_key(self, key):
if isinstance(key, str):
key = key.encode('utf-8')
if not isinstance(key, bytes):
raise InvalidKeyError("Key must be bytes or string")
if len(key) < self.MIN_KEY_LENGTH:
raise InvalidKeyError(f"Key must be at least {self.MIN_KEY_LENGTH} bytes")
return key
def sign(self, msg: bytes, key: bytes) -> bytes:
return hmac.new(key, msg, hashlib.sha256).digest()
def verify(self, msg: bytes, key: bytes, sig: bytes) -> bool:
expected = self.sign(msg, key)
return hmac.compare_digest(sig, expected)
# Usage with key validation
jwt.register_algorithm('SECURE-HMAC', SecureHMAC())
try:
# This will fail - key too short
jwt.encode({'data': 'test'}, 'short', algorithm='SECURE-HMAC')
except InvalidKeyError as e:
print(f"Key rejected: {e}")
# This will work - key meets minimum length
long_key = 'a' * 32
token = jwt.encode({'data': 'test'}, long_key, algorithm='SECURE-HMAC')Tools for discovering available algorithms and their capabilities:
import jwt
def list_available_algorithms():
"""List all globally available algorithms."""
jws = jwt.PyJWS()
algorithms = jws.get_algorithms()
for alg_name in sorted(algorithms):
try:
alg_obj = jwt.get_algorithm_by_name(alg_name)
print(f"{alg_name}: {type(alg_obj).__name__}")
except NotImplementedError as e:
print(f"{alg_name}: Not available ({e})")
def check_cryptography_algorithms():
"""Check which algorithms require cryptography library."""
from jwt.algorithms import requires_cryptography, has_crypto
print(f"Cryptography installed: {has_crypto}")
print(f"Algorithms requiring cryptography: {requires_cryptography}")
if not has_crypto:
print("Install cryptography for full algorithm support:")
print("pip install PyJWT[crypto]")
# Run diagnostics
list_available_algorithms()
check_cryptography_algorithms()Recommended patterns for secure algorithm management:
import jwt
# 1. Use explicit algorithm whitelists
PRODUCTION_ALGORITHMS = ['RS256', 'ES256', 'EdDSA']
def secure_decode(token, key):
return jwt.decode(token, key, algorithms=PRODUCTION_ALGORITHMS)
# 2. Block dangerous algorithms
BLOCKED_ALGORITHMS = ['none', 'HS256'] # Example policy
for alg in BLOCKED_ALGORITHMS:
try:
jwt.unregister_algorithm(alg)
except KeyError:
pass
# 3. Use asymmetric algorithms in production
def create_production_jwt_instance():
return jwt.PyJWT(algorithms=['RS256', 'ES256'])
# 4. Validate algorithm in token matches expected
def verify_with_algorithm_check(token, key, expected_algorithm):
header = jwt.get_unverified_header(token)
if header.get('alg') != expected_algorithm:
raise ValueError(f"Unexpected algorithm: {header.get('alg')}")
return jwt.decode(token, key, algorithms=[expected_algorithm])
# 5. Regular algorithm audit
def audit_algorithms():
"""Audit currently available algorithms for security compliance."""
available = jwt.PyJWS().get_algorithms()
dangerous = {'none'} # Example dangerous algorithms
weak = {'HS256', 'HS384', 'HS512'} # Example: symmetric keys
found_dangerous = set(available) & dangerous
found_weak = set(available) & weak
if found_dangerous:
print(f"CRITICAL: Dangerous algorithms available: {found_dangerous}")
if found_weak:
print(f"WARNING: Weak algorithms available: {found_weak}")
return len(found_dangerous) == 0 and len(found_weak) == 0
# Run security audit
if not audit_algorithms():
print("Algorithm security policy violations detected!")Install with Tessl CLI
npx tessl i tessl/pypi-pyjwt