CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyjwt

JSON Web Token implementation in Python with support for JWT, JWS, JWK, and JWKS

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

algorithm-management.mddocs/

Algorithm Management

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.

Capabilities

Global Algorithm Functions

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

Instance-Level Algorithm Management

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 instance

Usage 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())

Algorithm Security and Whitelisting

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

Algorithm Implementation Interface

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 key

Custom Algorithm Examples

Examples of implementing custom algorithms for specific use cases:

Custom HMAC with Different Hash Function

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

Custom Algorithm with Key Validation

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

Algorithm Discovery and Introspection

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()

Security Best Practices

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

docs

algorithm-management.md

index.md

jwk-operations.md

jwks-client.md

jws-operations.md

jwt-operations.md

tile.json