JOSE protocol implementation in Python with support for JSON Web Algorithms, Keys, and Signatures
73
Core interfaces and base classes providing JSON serialization and deserialization functionality for all JOSE objects. These interfaces define the contract for converting Python objects to/from JSON representations with proper error handling and type safety.
Base interface implemented by all JOSE objects for JSON serialization and deserialization with support for partial and full serialization modes.
class JSONDeSerializable:
"""Interface for JSON serializable/deserializable objects"""
def to_partial_json(self) -> Any:
"""
Perform partial serialization to JSON-compatible objects.
Partial serialization may include other JSONDeSerializable objects
that need further processing for full serialization.
Returns:
Any: Partially serialized object (may contain JSONDeSerializable instances)
Raises:
josepy.errors.SerializationError: If serialization fails
"""
def to_json(self) -> Any:
"""
Perform full serialization to basic Python types only.
Recursively serializes all JSONDeSerializable objects to basic types
suitable for JSON encoding (dict, list, str, int, float, bool, None).
Returns:
Any: Fully serialized object containing only basic Python types
Raises:
josepy.errors.SerializationError: If serialization fails
"""
@classmethod
def from_json(cls, jobj: Any) -> 'JSONDeSerializable':
"""
Deserialize from JSON-compatible Python object.
Parameters:
- jobj: Python object from json.loads() or equivalent
Returns:
JSONDeSerializable: Deserialized object instance
Raises:
josepy.errors.DeserializationError: If deserialization fails
"""
@classmethod
def json_loads(cls, json_string: Union[str, bytes]) -> 'JSONDeSerializable':
"""
Deserialize from JSON document string.
Parameters:
- json_string: JSON document as string or bytes
Returns:
JSONDeSerializable: Deserialized object instance
Raises:
josepy.errors.DeserializationError: If JSON parsing or deserialization fails
"""
def json_dumps(self, **kwargs) -> str:
"""
Serialize to JSON document string.
Parameters:
- **kwargs: Additional arguments passed to json.dumps()
Returns:
str: JSON document string
"""
def json_dumps_pretty(self) -> str:
"""
Serialize to pretty-formatted JSON string.
Returns:
str: Pretty-formatted JSON document with indentation and sorting
"""
@classmethod
def json_dump_default(cls, python_object: 'JSONDeSerializable') -> Any:
"""
Default serializer function for json.dumps().
This method is used as the 'default' parameter for json.dumps()
to handle JSONDeSerializable objects.
Parameters:
- python_object: Object to serialize
Returns:
Any: Serialized representation
Raises:
TypeError: If object is not JSONDeSerializable
"""from josepy import JWK, JWKRSA
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
# Create a JWK (which implements JSONDeSerializable)
private_key = rsa.generate_private_key(65537, 2048, default_backend())
jwk = JWKRSA(key=private_key)
# Serialize to JSON string
jwk_json = jwk.json_dumps()
print(f"JWK JSON: {jwk_json}")
# Pretty print
pretty_json = jwk.json_dumps_pretty()
print(f"Pretty JWK:\n{pretty_json}")
# Deserialize from JSON
loaded_jwk = JWKRSA.json_loads(jwk_json)
assert jwk.thumbprint() == loaded_jwk.thumbprint()import json
from josepy import JWS, Header, RS256
# Create JWS (JSONDeSerializable object)
payload = b'{"user": "alice", "role": "admin"}'
jws = JWS.sign(payload, key=jwk, alg=RS256)
# Use custom JSON encoder
custom_json = json.dumps(jws, default=JWS.json_dump_default, indent=2)
print(f"Custom encoded JWS:\n{custom_json}")
# Load back
loaded_jws = JWS.from_json(json.loads(custom_json))# Demonstrate the difference between partial and full serialization
header = Header(alg=RS256, jwk=jwk.public_key())
# Partial serialization may contain JSONDeSerializable objects
partial = header.to_partial_json()
print(f"Partial serialization type of 'jwk': {type(partial.get('jwk'))}")
# Output: <class 'josepy.jwk.JWKRSA'>
# Full serialization contains only basic Python types
full = header.to_json()
print(f"Full serialization type of 'jwk': {type(full.get('jwk'))}")
# Output: <class 'dict'>from josepy.errors import DeserializationError, SerializationError
try:
# Invalid JSON structure
invalid_jwk = JWKRSA.json_loads('{"invalid": "structure"}')
except DeserializationError as e:
print(f"Deserialization failed: {e}")
try:
# Malformed JSON string
malformed_jwk = JWKRSA.json_loads('invalid json')
except DeserializationError as e:
print(f"JSON parsing failed: {e}")import json
from josepy import JWK, JWKRSA, Header, RS256
# List of JOSE objects
jose_objects = [
JWKRSA(key=private_key),
Header(alg=RS256, kid="key-1"),
# ... other JOSE objects
]
# Serialize list of JOSE objects
serialized = json.dumps(
jose_objects,
default=JSONDeSerializable.json_dump_default,
indent=2
)
print(f"Serialized JOSE objects:\n{serialized}")
# The serialized JSON can be loaded back using appropriate from_json methods
data = json.loads(serialized)
# Then reconstruct objects based on their typesPartial Serialization (to_partial_json()):
JSONDeSerializable objectsFull Serialization (to_json()):
json_dump_default() for non-JSONDeSerializable objectsAll JOSE objects (JWK, JWS, Header, etc.) implement this interface, providing:
json moduleInstall with Tessl CLI
npx tessl i tessl/pypi-josepyevals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10