import json from json import JSONEncoder, JSONDecoder from collections.abc import Collection from ._rfc7519.claims import ( convert_claims, Claims, ClaimsOption, BaseClaimsRegistry, JWTClaimsRegistry, ) from ._rfc7519.security import check_sensitive_data from .jws import ( JWSRegistry, serialize_compact, deserialize_compact, ) from .jwe import ( JWERegistry, encrypt_compact, decrypt_compact, ) from .jwk import KeyFlexible from .errors import InvalidPayloadError from .util import to_bytes from .registry import Header __all__ = [ # types "Claims", "ClaimsOption", # modules "BaseClaimsRegistry", "JWTClaimsRegistry", "Token", # methods "encode", "decode", "check_sensitive_data", ] class Token: """The extracted token object, which contains ``header`` and ``claims``. :param header: the header part of the JWT :param claims: the payload part of the JWT """ def __init__(self, header: Header, claims: Claims): #: header in dict self.header = header #: payload claims in dict self.claims = claims def encode( header: Header, claims: Claims, key: KeyFlexible, algorithms: Collection[str] | None = None, registry: JWSRegistry | JWERegistry | None = None, encoder_cls: type[JSONEncoder] | None = None, default_type: str | None = "JWT", ) -> str: """Encode a JSON Web Token with the given header, and claims. :param header: A dict of the JWT header :param claims: A dict of the JWT claims to be encoded :param key: key used to sign the signature :param algorithms: a collection (list, tuple, or set) of allowed algorithms :param registry: a ``JWSRegistry`` or ``JWERegistry`` to use :param encoder_cls: A JSONEncoder subclass to use :param default_type: default value of the ``typ`` header parameter """ if default_type is not None: _header = {"typ": default_type, **header} else: _header = {**header} payload = convert_claims(claims, encoder_cls) if isinstance(registry, JWERegistry): return encrypt_compact(_header, payload, key, algorithms, registry) else: return serialize_compact(_header, payload, key, algorithms, registry) def decode( value: bytes | str, key: KeyFlexible, algorithms: Collection[str] | None = None, registry: JWSRegistry | JWERegistry | None = None, decoder_cls: type[JSONDecoder] | None = None, ) -> Token: """Decode the JSON Web Token string with the given key, and validate it with the claims requests. :param value: text of the JWT :param key: key used to verify the signature :param algorithms: a collection (list, tuple, or set) of allowed algorithms :param registry: a ``JWSRegistry`` or ``JWERegistry`` to use :param decoder_cls: A JSONDecoder subclass to use :raise BadSignatureError: when signature verification fails :raise InvalidPayloadError: when payload is not a valid JSON object """ _value = to_bytes(value) header: Header payload: bytes if isinstance(registry, JWERegistry): header, payload = _decode_jwe(_value, key, algorithms, registry) else: header, payload = _decode_jws(_value, key, algorithms, registry) try: claims: Claims = json.loads(payload, cls=decoder_cls) except (TypeError, ValueError): raise InvalidPayloadError() return Token(header, claims) def _decode_jwe( value: bytes, key: KeyFlexible, algorithms: Collection[str] | None = None, registry: JWERegistry | None = None ) -> tuple[Header, bytes]: jwe_obj = decrypt_compact(value, key, algorithms, registry) assert jwe_obj.plaintext is not None return jwe_obj.headers(), jwe_obj.plaintext def _decode_jws( value: bytes, key: KeyFlexible, algorithms: Collection[str] | None = None, registry: JWSRegistry | None = None ) -> tuple[Header, bytes]: jws_obj = deserialize_compact(value, key, algorithms, registry) assert jws_obj.payload is not None return jws_obj.headers(), jws_obj.payload