Skip to content

back to OpenHands summary

OpenHands: pyjwt

Pytest Summary for test tests

status count
failed 96
passed 163
skipped 2
xfailed 1
total 262
collected 262

Failed pytests:

test_advisory.py::TestAdvisory::test_ghsa_ffqj_6fqr_9h24

test_advisory.py::TestAdvisory::test_ghsa_ffqj_6fqr_9h24
self = 
jwt = b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0ZXN0IjoxMjM0fQ.6ulDpqSlbHmQ8bZXhZRLFko9SwcHrghCwh8d-exJEE4'
key = b'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPL1I9oiq+B8crkmuV4YViiUnhdLjCp3hvy1bNGuGfNL'
algorithms = ['none', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', ...]
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0ZXN0IjoxMjM0fQ'
crypto_segment = b'6ulDpqSlbHmQ8bZXhZRLFko9SwcHrghCwh8d-exJEE4'

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
            raise InvalidAlgorithmError('The specified alg value is not allowed')

        if alg == 'none':
            if merged_options['verify_signature']:
                raise DecodeError('Algorithm "none" not allowed')
            if key not in [None, '', 'none']:
                raise InvalidKeyError('When alg = "none", key must be empty or "none"')
            if signature != b'':
                raise InvalidSignatureError('Signature verification failed')
            return {
                'header': header,
                'payload': payload,
                'signature': signature
            }

        try:
            alg_obj = self._algorithms[alg]
        except KeyError:
            raise InvalidAlgorithmError('Algorithm not supported')

        if merged_options['verify_signature']:
            try:
                if key is None:
                    raise InvalidKeyError('Key is required when algorithm is not "none"')
>               key = alg_obj.prepare_key(key)

jwt/api_jws.py:302: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
key = b'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPL1I9oiq+B8crkmuV4YViiUnhdLjCp3hvy1bNGuGfNL'

    def prepare_key(self, key: Union[str, bytes]) -> bytes:
        if not isinstance(key, (str, bytes)):
            raise TypeError('Expected a string value')
        key = force_bytes(key)
        if is_pem_format(key) or is_ssh_key(key):
>           raise InvalidKeyError('The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.')
E           jwt.exceptions.InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.

jwt/algorithms.py:168: InvalidKeyError

During handling of the above exception, another exception occurred:

self = 

    @crypto_required
    def test_ghsa_ffqj_6fqr_9h24(self):
        # Generate ed25519 private key
        # private_key = ed25519.Ed25519PrivateKey.generate()

        # Get private key bytes as they would be stored in a file
        # priv_key_bytes = private_key.private_bytes(
        #     encoding=serialization.Encoding.PEM,
        #     format=serialization.PrivateFormat.PKCS8,
        #     encryption_algorithm=serialization.NoEncryption(),
        # )

        # Get public key bytes as they would be stored in a file
        # pub_key_bytes = private_key.public_key().public_bytes(
        #     encoding=serialization.Encoding.OpenSSH,
        #     format=serialization.PublicFormat.OpenSSH,
        # )

        # Making a good jwt token that should work by signing it
        # with the private key
        # encoded_good = jwt.encode({"test": 1234}, priv_key_bytes, algorithm="EdDSA")
        encoded_good = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0IjoxMjM0fQ.M5y1EEavZkHSlj9i8yi9nXKKyPBSAUhDRTOYZi3zZY11tZItDaR3qwAye8pc74_lZY3Ogt9KPNFbVOSGnUBHDg"

        # Using HMAC with the public key to trick the receiver to think that the
        # public key is a HMAC secret
        encoded_bad = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0ZXN0IjoxMjM0fQ.6ulDpqSlbHmQ8bZXhZRLFko9SwcHrghCwh8d-exJEE4"

        algorithm_names = list(get_default_algorithms())

        # Both of the jwt tokens are validated as valid
        jwt.decode(
            encoded_good,
            pub_key_bytes,
            algorithms=algorithm_names,
        )

        with pytest.raises(InvalidKeyError):
>           jwt.decode(
                encoded_bad,
                pub_key_bytes,
                algorithms=algorithm_names,
            )

tests/test_advisory.py:64: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0ZXN0IjoxMjM0fQ.6ulDpqSlbHmQ8bZXhZRLFko9SwcHrghCwh8d-exJEE4'
key = b'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPL1I9oiq+B8crkmuV4YViiUnhdLjCp3hvy1bNGuGfNL'
algorithms = ['none', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', ...]
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0ZXN0IjoxMjM0fQ'
crypto_segment = b'6ulDpqSlbHmQ8bZXhZRLFko9SwcHrghCwh8d-exJEE4'

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
            raise InvalidAlgorithmError('The specified alg value is not allowed')

        if alg == 'none':
            if merged_options['verify_signature']:
                raise DecodeError('Algorithm "none" not allowed')
            if key not in [None, '', 'none']:
                raise InvalidKeyError('When alg = "none", key must be empty or "none"')
            if signature != b'':
                raise InvalidSignatureError('Signature verification failed')
            return {
                'header': header,
                'payload': payload,
                'signature': signature
            }

        try:
            alg_obj = self._algorithms[alg]
        except KeyError:
            raise InvalidAlgorithmError('Algorithm not supported')

        if merged_options['verify_signature']:
            try:
                if key is None:
                    raise InvalidKeyError('Key is required when algorithm is not "none"')
                key = alg_obj.prepare_key(key)
>           except InvalidKeyError:
E           NameError: name 'InvalidKeyError' is not defined

jwt/api_jws.py:303: NameError

test_algorithms.py::TestAlgorithms::test_ec_jwk_public_and_private_keys_should_parse_and_verify

test_algorithms.py::TestAlgorithms::test_ec_jwk_public_and_private_keys_should_parse_and_verify
self = 

    @crypto_required
    def test_ec_jwk_public_and_private_keys_should_parse_and_verify(self):
        tests = {
            "P-256": ECAlgorithm.SHA256,
            "P-384": ECAlgorithm.SHA384,
            "P-521": ECAlgorithm.SHA512,
            "secp256k1": ECAlgorithm.SHA256,
        }
        for curve, hash in tests.items():
            algo = ECAlgorithm(hash)

            with open(key_path(f"jwk_ec_pub_{curve}.json")) as keyfile:
>               pub_key = cast(EllipticCurvePublicKey, algo.from_jwk(keyfile.read()))

tests/test_algorithms.py:174: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

jwk = {'crv': 'secp256k1', 'kid': 'bilbo.baggins.256k@hobbiton.example', 'kty': 'EC', 'x': 'MLnVyPDPQpNm0KaaO4iEh0i8JItHXJE0NcIe8GK1SYs', ...}

    @staticmethod
    def from_jwk(jwk: Union[str, JWKDict]) -> Union[EllipticCurvePrivateKey, EllipticCurvePublicKey]:
        if isinstance(jwk, str):
            jwk = json.loads(jwk)
        if not isinstance(jwk, dict):
            raise InvalidKeyError('Key must be a dict or a string')
        if jwk.get('kty') != 'EC':
            raise InvalidKeyError('Not an EC key')

>       curve = {
            'P-256K': SECP256K1,
            'P-256': SECP256R1,
            'P-384': SECP384R1,
            'P-521': SECP521R1
        }[jwk['crv']]()
E       KeyError: 'secp256k1'

jwt/algorithms.py:421: KeyError

test_algorithms.py::TestAlgorithms::test_ec_jwk_fails_on_invalid_json

test_algorithms.py::TestAlgorithms::test_ec_jwk_fails_on_invalid_json
self = 

    @crypto_required
    def test_ec_jwk_fails_on_invalid_json(self):
        algo = ECAlgorithm(ECAlgorithm.SHA512)

        valid_points = {
            "P-256": {
                "x": "PTTjIY84aLtaZCxLTrG_d8I0G6YKCV7lg8M4xkKfwQ4",
                "y": "ank6KA34vv24HZLXlChVs85NEGlpg2sbqNmR_BcgyJU",
            },
            "P-384": {
                "x": "IDC-5s6FERlbC4Nc_4JhKW8sd51AhixtMdNUtPxhRFP323QY6cwWeIA3leyZhz-J",
                "y": "eovmN9ocANS8IJxDAGSuC1FehTq5ZFLJU7XSPg36zHpv4H2byKGEcCBiwT4sFJsy",
            },
            "P-521": {
                "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt",
                "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1",
            },
            "secp256k1": {
                "x": "MLnVyPDPQpNm0KaaO4iEh0i8JItHXJE0NcIe8GK1SYs",
                "y": "7r8d-xF7QAgT5kSRdly6M8xeg4Jz83Gs_CQPQRH65QI",
            },
        }

        # Invalid JSON
        with pytest.raises(InvalidKeyError):
>           algo.from_jwk("")

tests/test_algorithms.py:207: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/algorithms.py:415: in from_jwk
    jwk = json.loads(jwk)
/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/json/__init__.py:346: in loads
    return _default_decoder.decode(s)
/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/json/decoder.py:337: in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
s = "", idx = 0

    def raw_decode(self, s, idx=0):
        """Decode a JSON document from ``s`` (a ``str`` beginning with
        a JSON document) and return a 2-tuple of the Python
        representation and the index in ``s`` where the document ended.

        This can be used to decode a JSON document from a string that may
        have extraneous data at the end.

        """
        try:
            obj, end = self.scan_once(s, idx)
        except StopIteration as err:
>           raise JSONDecodeError("Expecting value", s, err.value) from None
E           json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/json/decoder.py:355: JSONDecodeError

test_algorithms.py::TestAlgorithms::test_ec_to_jwk_raises_exception_on_invalid_key

test_algorithms.py::TestAlgorithms::test_ec_to_jwk_raises_exception_on_invalid_key
self = 

    @crypto_required
    def test_ec_to_jwk_raises_exception_on_invalid_key(self):
        algo = ECAlgorithm(ECAlgorithm.SHA256)

        with pytest.raises(InvalidKeyError):
>           algo.to_jwk({"not": "a valid key"})  # type: ignore[call-overload]

tests/test_algorithms.py:317: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

key_obj = {'not': 'a valid key'}, as_dict = False

    @staticmethod
    def to_jwk(key_obj: Union[EllipticCurvePrivateKey, EllipticCurvePublicKey], as_dict: bool = False) -> Union[str, JWKDict]:
        if isinstance(key_obj, EllipticCurvePrivateKey):
            numbers = key_obj.private_numbers()
            jwk = {
                'kty': 'EC',
                'crv': {
                    SECP256K1: 'P-256K',
                    SECP256R1: 'P-256',
                    SECP384R1: 'P-384',
                    SECP521R1: 'P-521'
                }[type(numbers.public_numbers.curve)],
                'x': to_base64url_uint(numbers.public_numbers.x).decode('ascii'),
                'y': to_base64url_uint(numbers.public_numbers.y).decode('ascii'),
                'd': to_base64url_uint(numbers.private_value).decode('ascii')
            }
        else:
>           numbers = key_obj.public_numbers()
E           AttributeError: 'dict' object has no attribute 'public_numbers'

jwt/algorithms.py:395: AttributeError

test_algorithms.py::TestAlgorithms::test_ec_to_jwk_with_valid_curves[False]

test_algorithms.py::TestAlgorithms::test_ec_to_jwk_with_valid_curves[False]
self = 
as_dict = False

    @crypto_required
    @pytest.mark.parametrize("as_dict", (False, True))
    def test_ec_to_jwk_with_valid_curves(self, as_dict):
        tests = {
            "P-256": ECAlgorithm.SHA256,
            "P-384": ECAlgorithm.SHA384,
            "P-521": ECAlgorithm.SHA512,
            "secp256k1": ECAlgorithm.SHA256,
        }
        for curve, hash in tests.items():
            algo = ECAlgorithm(hash)

            with open(key_path(f"jwk_ec_pub_{curve}.json")) as keyfile:
>               pub_key = algo.from_jwk(keyfile.read())

tests/test_algorithms.py:332: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

jwk = {'crv': 'secp256k1', 'kid': 'bilbo.baggins.256k@hobbiton.example', 'kty': 'EC', 'x': 'MLnVyPDPQpNm0KaaO4iEh0i8JItHXJE0NcIe8GK1SYs', ...}

    @staticmethod
    def from_jwk(jwk: Union[str, JWKDict]) -> Union[EllipticCurvePrivateKey, EllipticCurvePublicKey]:
        if isinstance(jwk, str):
            jwk = json.loads(jwk)
        if not isinstance(jwk, dict):
            raise InvalidKeyError('Key must be a dict or a string')
        if jwk.get('kty') != 'EC':
            raise InvalidKeyError('Not an EC key')

>       curve = {
            'P-256K': SECP256K1,
            'P-256': SECP256R1,
            'P-384': SECP384R1,
            'P-521': SECP521R1
        }[jwk['crv']]()
E       KeyError: 'secp256k1'

jwt/algorithms.py:421: KeyError

test_algorithms.py::TestAlgorithms::test_ec_to_jwk_with_valid_curves[True]

test_algorithms.py::TestAlgorithms::test_ec_to_jwk_with_valid_curves[True]
self = 
as_dict = True

    @crypto_required
    @pytest.mark.parametrize("as_dict", (False, True))
    def test_ec_to_jwk_with_valid_curves(self, as_dict):
        tests = {
            "P-256": ECAlgorithm.SHA256,
            "P-384": ECAlgorithm.SHA384,
            "P-521": ECAlgorithm.SHA512,
            "secp256k1": ECAlgorithm.SHA256,
        }
        for curve, hash in tests.items():
            algo = ECAlgorithm(hash)

            with open(key_path(f"jwk_ec_pub_{curve}.json")) as keyfile:
>               pub_key = algo.from_jwk(keyfile.read())

tests/test_algorithms.py:332: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

jwk = {'crv': 'secp256k1', 'kid': 'bilbo.baggins.256k@hobbiton.example', 'kty': 'EC', 'x': 'MLnVyPDPQpNm0KaaO4iEh0i8JItHXJE0NcIe8GK1SYs', ...}

    @staticmethod
    def from_jwk(jwk: Union[str, JWKDict]) -> Union[EllipticCurvePrivateKey, EllipticCurvePublicKey]:
        if isinstance(jwk, str):
            jwk = json.loads(jwk)
        if not isinstance(jwk, dict):
            raise InvalidKeyError('Key must be a dict or a string')
        if jwk.get('kty') != 'EC':
            raise InvalidKeyError('Not an EC key')

>       curve = {
            'P-256K': SECP256K1,
            'P-256': SECP256R1,
            'P-384': SECP384R1,
            'P-521': SECP521R1
        }[jwk['crv']]()
E       KeyError: 'secp256k1'

jwt/algorithms.py:421: KeyError

test_algorithms.py::TestAlgorithms::test_ec_to_jwk_with_invalid_curve

test_algorithms.py::TestAlgorithms::test_ec_to_jwk_with_invalid_curve
self = 

    @crypto_required
    def test_ec_to_jwk_with_invalid_curve(self):
        algo = ECAlgorithm(ECAlgorithm.SHA256)

        with open(key_path("testkey_ec_secp192r1.priv")) as keyfile:
            priv_key = algo.prepare_key(keyfile.read())

        with pytest.raises(InvalidKeyError):
>           algo.to_jwk(priv_key)

tests/test_algorithms.py:357: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

key_obj = 
as_dict = False

    @staticmethod
    def to_jwk(key_obj: Union[EllipticCurvePrivateKey, EllipticCurvePublicKey], as_dict: bool = False) -> Union[str, JWKDict]:
        if isinstance(key_obj, EllipticCurvePrivateKey):
            numbers = key_obj.private_numbers()
            jwk = {
                'kty': 'EC',
>               'crv': {
                    SECP256K1: 'P-256K',
                    SECP256R1: 'P-256',
                    SECP384R1: 'P-384',
                    SECP521R1: 'P-521'
                }[type(numbers.public_numbers.curve)],
                'x': to_base64url_uint(numbers.public_numbers.x).decode('ascii'),
                'y': to_base64url_uint(numbers.public_numbers.y).decode('ascii'),
                'd': to_base64url_uint(numbers.private_value).decode('ascii')
            }
E           KeyError: 

jwt/algorithms.py:384: KeyError

test_algorithms.py::TestAlgorithms::test_rsa_jwk_private_key_with_other_primes_is_invalid

test_algorithms.py::TestAlgorithms::test_rsa_jwk_private_key_with_other_primes_is_invalid
self = 

    @crypto_required
    def test_rsa_jwk_private_key_with_other_primes_is_invalid(self):
        algo = RSAAlgorithm(RSAAlgorithm.SHA256)

        with open(key_path("jwk_rsa_key.json")) as keyfile:
>           with pytest.raises(InvalidKeyError):
E           Failed: DID NOT RAISE 

tests/test_algorithms.py:401: Failed

test_algorithms.py::TestAlgorithms::test_rsa_jwk_private_key_with_missing_values_is_invalid

test_algorithms.py::TestAlgorithms::test_rsa_jwk_private_key_with_missing_values_is_invalid
self = 

    @crypto_required
    def test_rsa_jwk_private_key_with_missing_values_is_invalid(self):
        algo = RSAAlgorithm(RSAAlgorithm.SHA256)

        with open(key_path("jwk_rsa_key.json")) as keyfile:
>           with pytest.raises(InvalidKeyError):
E           Failed: DID NOT RAISE 

tests/test_algorithms.py:412: Failed

test_algorithms.py::TestAlgorithms::test_rsa_jwk_private_key_can_recover_prime_factors

test_algorithms.py::TestAlgorithms::test_rsa_jwk_private_key_can_recover_prime_factors
self = 

    @crypto_required
    def test_rsa_jwk_private_key_can_recover_prime_factors(self):
        algo = RSAAlgorithm(RSAAlgorithm.SHA256)

        with open(key_path("jwk_rsa_key.json")) as keyfile:
            keybytes = keyfile.read()
            control_key = cast(RSAPrivateKey, algo.from_jwk(keybytes)).private_numbers()

            keydata = json.loads(keybytes)
            delete_these = ["p", "q", "dp", "dq", "qi"]
            for field in delete_these:
                del keydata[field]

            parsed_key = cast(
                RSAPrivateKey, algo.from_jwk(json.dumps(keydata))
>           ).private_numbers()
E           AttributeError: 'cryptography.hazmat.bindings._rust.openssl.rsa.RSAPublicKey' object has no attribute 'private_numbers'

tests/test_algorithms.py:433: AttributeError

test_algorithms.py::TestAlgorithms::test_rsa_jwk_private_key_with_missing_required_values_is_invalid

test_algorithms.py::TestAlgorithms::test_rsa_jwk_private_key_with_missing_required_values_is_invalid
self = 

    @crypto_required
    def test_rsa_jwk_private_key_with_missing_required_values_is_invalid(self):
        algo = RSAAlgorithm(RSAAlgorithm.SHA256)

        with open(key_path("jwk_rsa_key.json")) as keyfile:
>           with pytest.raises(InvalidKeyError):
E           Failed: DID NOT RAISE 

tests/test_algorithms.py:447: Failed

test_algorithms.py::TestAlgorithms::test_rsa_jwk_raises_exception_if_not_a_valid_key

test_algorithms.py::TestAlgorithms::test_rsa_jwk_raises_exception_if_not_a_valid_key
self = 

    @crypto_required
    def test_rsa_jwk_raises_exception_if_not_a_valid_key(self):
        algo = RSAAlgorithm(RSAAlgorithm.SHA256)

        # Invalid JSON
        with pytest.raises(InvalidKeyError):
>           algo.from_jwk("{not-a-real-key")

tests/test_algorithms.py:459: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/algorithms.py:293: in from_jwk
    jwk = json.loads(jwk)
/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/json/__init__.py:346: in loads
    return _default_decoder.decode(s)
/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/json/decoder.py:337: in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
s = '{not-a-real-key', idx = 0

    def raw_decode(self, s, idx=0):
        """Decode a JSON document from ``s`` (a ``str`` beginning with
        a JSON document) and return a 2-tuple of the Python
        representation and the index in ``s`` where the document ended.

        This can be used to decode a JSON document from a string that may
        have extraneous data at the end.

        """
        try:
>           obj, end = self.scan_once(s, idx)
E           json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/json/decoder.py:353: JSONDecodeError

test_algorithms.py::TestAlgorithms::test_rsa_to_jwk_returns_correct_values_for_public_key[False]

test_algorithms.py::TestAlgorithms::test_rsa_to_jwk_returns_correct_values_for_public_key[False]
self = 
as_dict = False

    @crypto_required
    @pytest.mark.parametrize("as_dict", (False, True))
    def test_rsa_to_jwk_returns_correct_values_for_public_key(self, as_dict):
        algo = RSAAlgorithm(RSAAlgorithm.SHA256)

        with open(key_path("testkey_rsa.pub")) as keyfile:
            pub_key = algo.prepare_key(keyfile.read())

        key: Any = algo.to_jwk(pub_key, as_dict=as_dict)

        if not as_dict:
            key = json.loads(key)

        expected = {
            "e": "AQAB",
            "key_ops": ["verify"],
            "kty": "RSA",
            "n": (
                "1HgzBfJv2cOjQryCwe8NEelriOTNFWKZUivevUrRhlqcmZJdCvuCJRr-xCN-"
                "OmO8qwgJJR98feNujxVg-J9Ls3_UOA4HcF9nYH6aqVXELAE8Hk_ALvxi96ms"
                "1DDuAvQGaYZ-lANxlvxeQFOZSbjkz_9mh8aLeGKwqJLp3p-OhUBQpwvAUAPg"
                "82-OUtgTW3nSljjeFr14B8qAneGSc_wl0ni--1SRZUXFSovzcqQOkla3W27r"
                "rLfrD6LXgj_TsDs4vD1PnIm1zcVenKT7TfYI17bsG_O_Wecwz2Nl19pL7gDo"
                "sNruF3ogJWNq1Lyn_ijPQnkPLpZHyhvuiycYcI3DiQ"
            ),
        }
>       assert key == expected
E       AssertionError: assert {'e': 'AQAB',...vuiycYcI3DiQ'} == {'e': 'AQAB',...vuiycYcI3DiQ'}
E         
E         Omitting 3 identical items, use -vv to show
E         Right contains 1 more item:
E         {'key_ops': ['verify']}
E         Use -v to get more diff

tests/test_algorithms.py:491: AssertionError

test_algorithms.py::TestAlgorithms::test_rsa_to_jwk_returns_correct_values_for_public_key[True]

test_algorithms.py::TestAlgorithms::test_rsa_to_jwk_returns_correct_values_for_public_key[True]
self = 
as_dict = True

    @crypto_required
    @pytest.mark.parametrize("as_dict", (False, True))
    def test_rsa_to_jwk_returns_correct_values_for_public_key(self, as_dict):
        algo = RSAAlgorithm(RSAAlgorithm.SHA256)

        with open(key_path("testkey_rsa.pub")) as keyfile:
            pub_key = algo.prepare_key(keyfile.read())

        key: Any = algo.to_jwk(pub_key, as_dict=as_dict)

        if not as_dict:
            key = json.loads(key)

        expected = {
            "e": "AQAB",
            "key_ops": ["verify"],
            "kty": "RSA",
            "n": (
                "1HgzBfJv2cOjQryCwe8NEelriOTNFWKZUivevUrRhlqcmZJdCvuCJRr-xCN-"
                "OmO8qwgJJR98feNujxVg-J9Ls3_UOA4HcF9nYH6aqVXELAE8Hk_ALvxi96ms"
                "1DDuAvQGaYZ-lANxlvxeQFOZSbjkz_9mh8aLeGKwqJLp3p-OhUBQpwvAUAPg"
                "82-OUtgTW3nSljjeFr14B8qAneGSc_wl0ni--1SRZUXFSovzcqQOkla3W27r"
                "rLfrD6LXgj_TsDs4vD1PnIm1zcVenKT7TfYI17bsG_O_Wecwz2Nl19pL7gDo"
                "sNruF3ogJWNq1Lyn_ijPQnkPLpZHyhvuiycYcI3DiQ"
            ),
        }
>       assert key == expected
E       AssertionError: assert {'e': 'AQAB',...vuiycYcI3DiQ'} == {'e': 'AQAB',...vuiycYcI3DiQ'}
E         
E         Omitting 3 identical items, use -vv to show
E         Right contains 1 more item:
E         {'key_ops': ['verify']}
E         Use -v to get more diff

tests/test_algorithms.py:491: AssertionError

test_algorithms.py::TestAlgorithms::test_rsa_to_jwk_returns_correct_values_for_private_key[False]

test_algorithms.py::TestAlgorithms::test_rsa_to_jwk_returns_correct_values_for_private_key[False]
self = 
as_dict = False

    @crypto_required
    @pytest.mark.parametrize("as_dict", (False, True))
    def test_rsa_to_jwk_returns_correct_values_for_private_key(self, as_dict):
        algo = RSAAlgorithm(RSAAlgorithm.SHA256)

        with open(key_path("testkey_rsa.priv")) as keyfile:
            priv_key = algo.prepare_key(keyfile.read())

        key: Any = algo.to_jwk(priv_key, as_dict=as_dict)

        if not as_dict:
            key = json.loads(key)

        expected = {
            "key_ops": ["sign"],
            "kty": "RSA",
            "e": "AQAB",
            "n": (
                "1HgzBfJv2cOjQryCwe8NEelriOTNFWKZUivevUrRhlqcmZJdCvuCJRr-xCN-"
                "OmO8qwgJJR98feNujxVg-J9Ls3_UOA4HcF9nYH6aqVXELAE8Hk_ALvxi96ms"
                "1DDuAvQGaYZ-lANxlvxeQFOZSbjkz_9mh8aLeGKwqJLp3p-OhUBQpwvAUAPg"
                "82-OUtgTW3nSljjeFr14B8qAneGSc_wl0ni--1SRZUXFSovzcqQOkla3W27r"
                "rLfrD6LXgj_TsDs4vD1PnIm1zcVenKT7TfYI17bsG_O_Wecwz2Nl19pL7gDo"
                "sNruF3ogJWNq1Lyn_ijPQnkPLpZHyhvuiycYcI3DiQ"
            ),
            "d": (
                "rfbs8AWdB1RkLJRlC51LukrAvYl5UfU1TE6XRa4o-DTg2-03OXLNEMyVpMr"
                "a47weEnu14StypzC8qXL7vxXOyd30SSFTffLfleaTg-qxgMZSDw-Fb_M-pU"
                "HMPMEDYG-lgGma4l4fd1yTX2ATtoUo9BVOQgWS1LMZqi0ASEOkUfzlBgL04"
                "UoaLhPSuDdLygdlDzgruVPnec0t1uOEObmrcWIkhwU2CGQzeLtuzX6OVgPh"
                "k7xcnjbDurTTVpWH0R0gbZ5ukmQ2P-YuCX8T9iWNMGjPNSkb7h02s2Oe9ZR"
                "zP007xQ0VF-Z7xyLuxk6ASmoX1S39ujSbk2WF0eXNPRgFwQ"
            ),
            "q": (
                "47hlW2f1ARuWYJf9Dl6MieXjdj2dGx9PL2UH0unVzJYInd56nqXNPrQrc5k"
                "ZU65KApC9n9oKUwIxuqwAAbh8oGNEQDqnuTj-powCkdC6bwA8KH1Y-wotpq"
                "_GSjxkNzjWRm2GArJSzZc6Fb8EuObOrAavKJ285-zMPCEfus1WZG0"
            ),
            "p": (
                "7tr0z929Lp4OHIRJjIKM_rDrWMPtRgnV-51pgWsN6qdpDzns_PgFwrHcoyY"
                "sWIO-4yCdVWPxFOgEZ8xXTM_uwOe4VEmdZhw55Tx7axYZtmZYZbO_RIP4CG"
                "mlJlOFTiYnxpr-2Cx6kIeQmd-hf7fA3tL018aEzwYMbFMcnAGnEg0"
            ),
            "qi": (
                "djo95mB0LVYikNPa-NgyDwLotLqrueb9IviMmn6zKHCwiOXReqXDX9slB8"
                "RA15uv56bmN04O__NyVFcgJ2ef169GZHiRFIgIy0Pl8LYkMhCYKKhyqM7g"
                "xN-SqGqDTKDC22j00S7jcvCaa1qadn1qbdfukZ4NXv7E2d_LO0Y2Kkc"
            ),
            "dp": (
                "tgZ2-tJpEdWxu1m1EzeKa644LHVjpTRptk7H0LDc8i6SieADEuWQvkb9df"
                "fpY6tDFaQNQr3fQ6dtdAztmsP7l1b_ynwvT1nDZUcqZvl4ruBgDWFmKbjI"
                "lOCt0v9jX6MEPP5xqBx9axdkw18BnGtUuHrbzHSlUX-yh_rumpVH1SE"
            ),
            "dq": (
                "xxCIuhD0YlWFbUcwFgGdBWcLIm_WCMGj7SB6aGu1VDTLr4Wu10TFWM0TNu"
                "hc9YPker2gpj5qzAmdAzwcfWSSvXpJTYR43jfulBTMoj8-2o3wCM0anclW"
                "AuKhin-kc4mh9ssDXRQZwlMymZP0QtaxUDw_nlfVrUCZgO7L1_ZsUTk"
            ),
        }
>       assert key == expected
E       AssertionError: assert {'d': 'rfbs8A...: 'AQAB', ...} == {'d': 'rfbs8A...: 'AQAB', ...}
E         
E         Omitting 9 identical items, use -vv to show
E         Right contains 1 more item:
E         {'key_ops': ['sign']}
E         Use -v to get more diff

tests/test_algorithms.py:552: AssertionError

test_algorithms.py::TestAlgorithms::test_rsa_to_jwk_returns_correct_values_for_private_key[True]

test_algorithms.py::TestAlgorithms::test_rsa_to_jwk_returns_correct_values_for_private_key[True]
self = 
as_dict = True

    @crypto_required
    @pytest.mark.parametrize("as_dict", (False, True))
    def test_rsa_to_jwk_returns_correct_values_for_private_key(self, as_dict):
        algo = RSAAlgorithm(RSAAlgorithm.SHA256)

        with open(key_path("testkey_rsa.priv")) as keyfile:
            priv_key = algo.prepare_key(keyfile.read())

        key: Any = algo.to_jwk(priv_key, as_dict=as_dict)

        if not as_dict:
            key = json.loads(key)

        expected = {
            "key_ops": ["sign"],
            "kty": "RSA",
            "e": "AQAB",
            "n": (
                "1HgzBfJv2cOjQryCwe8NEelriOTNFWKZUivevUrRhlqcmZJdCvuCJRr-xCN-"
                "OmO8qwgJJR98feNujxVg-J9Ls3_UOA4HcF9nYH6aqVXELAE8Hk_ALvxi96ms"
                "1DDuAvQGaYZ-lANxlvxeQFOZSbjkz_9mh8aLeGKwqJLp3p-OhUBQpwvAUAPg"
                "82-OUtgTW3nSljjeFr14B8qAneGSc_wl0ni--1SRZUXFSovzcqQOkla3W27r"
                "rLfrD6LXgj_TsDs4vD1PnIm1zcVenKT7TfYI17bsG_O_Wecwz2Nl19pL7gDo"
                "sNruF3ogJWNq1Lyn_ijPQnkPLpZHyhvuiycYcI3DiQ"
            ),
            "d": (
                "rfbs8AWdB1RkLJRlC51LukrAvYl5UfU1TE6XRa4o-DTg2-03OXLNEMyVpMr"
                "a47weEnu14StypzC8qXL7vxXOyd30SSFTffLfleaTg-qxgMZSDw-Fb_M-pU"
                "HMPMEDYG-lgGma4l4fd1yTX2ATtoUo9BVOQgWS1LMZqi0ASEOkUfzlBgL04"
                "UoaLhPSuDdLygdlDzgruVPnec0t1uOEObmrcWIkhwU2CGQzeLtuzX6OVgPh"
                "k7xcnjbDurTTVpWH0R0gbZ5ukmQ2P-YuCX8T9iWNMGjPNSkb7h02s2Oe9ZR"
                "zP007xQ0VF-Z7xyLuxk6ASmoX1S39ujSbk2WF0eXNPRgFwQ"
            ),
            "q": (
                "47hlW2f1ARuWYJf9Dl6MieXjdj2dGx9PL2UH0unVzJYInd56nqXNPrQrc5k"
                "ZU65KApC9n9oKUwIxuqwAAbh8oGNEQDqnuTj-powCkdC6bwA8KH1Y-wotpq"
                "_GSjxkNzjWRm2GArJSzZc6Fb8EuObOrAavKJ285-zMPCEfus1WZG0"
            ),
            "p": (
                "7tr0z929Lp4OHIRJjIKM_rDrWMPtRgnV-51pgWsN6qdpDzns_PgFwrHcoyY"
                "sWIO-4yCdVWPxFOgEZ8xXTM_uwOe4VEmdZhw55Tx7axYZtmZYZbO_RIP4CG"
                "mlJlOFTiYnxpr-2Cx6kIeQmd-hf7fA3tL018aEzwYMbFMcnAGnEg0"
            ),
            "qi": (
                "djo95mB0LVYikNPa-NgyDwLotLqrueb9IviMmn6zKHCwiOXReqXDX9slB8"
                "RA15uv56bmN04O__NyVFcgJ2ef169GZHiRFIgIy0Pl8LYkMhCYKKhyqM7g"
                "xN-SqGqDTKDC22j00S7jcvCaa1qadn1qbdfukZ4NXv7E2d_LO0Y2Kkc"
            ),
            "dp": (
                "tgZ2-tJpEdWxu1m1EzeKa644LHVjpTRptk7H0LDc8i6SieADEuWQvkb9df"
                "fpY6tDFaQNQr3fQ6dtdAztmsP7l1b_ynwvT1nDZUcqZvl4ruBgDWFmKbjI"
                "lOCt0v9jX6MEPP5xqBx9axdkw18BnGtUuHrbzHSlUX-yh_rumpVH1SE"
            ),
            "dq": (
                "xxCIuhD0YlWFbUcwFgGdBWcLIm_WCMGj7SB6aGu1VDTLr4Wu10TFWM0TNu"
                "hc9YPker2gpj5qzAmdAzwcfWSSvXpJTYR43jfulBTMoj8-2o3wCM0anclW"
                "AuKhin-kc4mh9ssDXRQZwlMymZP0QtaxUDw_nlfVrUCZgO7L1_ZsUTk"
            ),
        }
>       assert key == expected
E       AssertionError: assert {'d': 'rfbs8A...: 'AQAB', ...} == {'d': 'rfbs8A...: 'AQAB', ...}
E         
E         Omitting 9 identical items, use -vv to show
E         Right contains 1 more item:
E         {'key_ops': ['sign']}
E         Use -v to get more diff

tests/test_algorithms.py:552: AssertionError

test_algorithms.py::TestAlgorithms::test_rsa_to_jwk_raises_exception_on_invalid_key

test_algorithms.py::TestAlgorithms::test_rsa_to_jwk_raises_exception_on_invalid_key
self = 

    @crypto_required
    def test_rsa_to_jwk_raises_exception_on_invalid_key(self):
        algo = RSAAlgorithm(RSAAlgorithm.SHA256)

        with pytest.raises(InvalidKeyError):
>           algo.to_jwk({"not": "a valid key"})  # type: ignore[call-overload]

tests/test_algorithms.py:559: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

key_obj = {'not': 'a valid key'}, as_dict = False

    @staticmethod
    def to_jwk(key_obj: Union[RSAPrivateKey, RSAPublicKey], as_dict: bool = False) -> Union[str, JWKDict]:
        if isinstance(key_obj, RSAPrivateKey):
            numbers = key_obj.private_numbers()
            jwk = {
                'kty': 'RSA',
                'n': to_base64url_uint(numbers.public_numbers.n).decode('ascii'),
                'e': to_base64url_uint(numbers.public_numbers.e).decode('ascii'),
                'd': to_base64url_uint(numbers.d).decode('ascii'),
                'p': to_base64url_uint(numbers.p).decode('ascii'),
                'q': to_base64url_uint(numbers.q).decode('ascii'),
                'dp': to_base64url_uint(numbers.dmp1).decode('ascii'),
                'dq': to_base64url_uint(numbers.dmq1).decode('ascii'),
                'qi': to_base64url_uint(numbers.iqmp).decode('ascii')
            }
        else:
>           numbers = key_obj.public_numbers()
E           AttributeError: 'dict' object has no attribute 'public_numbers'

jwt/algorithms.py:279: AttributeError

test_algorithms.py::TestAlgorithms::test_ec_should_throw_exception_on_wrong_key

test_algorithms.py::TestAlgorithms::test_ec_should_throw_exception_on_wrong_key
self = 

    @crypto_required
    def test_ec_should_throw_exception_on_wrong_key(self):
        algo = ECAlgorithm(ECAlgorithm.SHA256)

>       with pytest.raises(InvalidKeyError):
E       Failed: DID NOT RAISE 

tests/test_algorithms.py:629: Failed

test_algorithms.py::TestOKPAlgorithms::test_okp_ed25519_should_reject_non_string_key

test_algorithms.py::TestOKPAlgorithms::test_okp_ed25519_should_reject_non_string_key
self = 

    def test_okp_ed25519_should_reject_non_string_key(self):
        algo = OKPAlgorithm()

        with pytest.raises(InvalidKeyError):
>           algo.prepare_key(None)  # type: ignore[arg-type]

tests/test_algorithms.py:813: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/algorithms.py:493: in prepare_key
    key = force_bytes(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

value = None

    def force_bytes(value: Union[str, bytes]) -> bytes:
        if isinstance(value, str):
            return value.encode('utf-8')
        elif isinstance(value, bytes):
            return value
        else:
>           raise TypeError('Expected string or bytes type')
E           TypeError: Expected string or bytes type

jwt/utils.py:17: TypeError

test_algorithms.py::TestOKPAlgorithms::test_okp_ed25519_jwk_fails_on_invalid_json

test_algorithms.py::TestOKPAlgorithms::test_okp_ed25519_jwk_fails_on_invalid_json
self = 

    def test_okp_ed25519_jwk_fails_on_invalid_json(self):
        algo = OKPAlgorithm()

        with open(key_path("jwk_okp_pub_Ed25519.json")) as keyfile:
            valid_pub = json.loads(keyfile.read())
        with open(key_path("jwk_okp_key_Ed25519.json")) as keyfile:
            valid_key = json.loads(keyfile.read())

        # Invalid instance type
        with pytest.raises(InvalidKeyError):
            algo.from_jwk(123)  # type: ignore[arg-type]

        # Invalid JSON
        with pytest.raises(InvalidKeyError):
>           algo.from_jwk("")

tests/test_algorithms.py:919: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/algorithms.py:592: in from_jwk
    jwk = json.loads(jwk)
/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/json/__init__.py:346: in loads
    return _default_decoder.decode(s)
/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/json/decoder.py:337: in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
s = "", idx = 0

    def raw_decode(self, s, idx=0):
        """Decode a JSON document from ``s`` (a ``str`` beginning with
        a JSON document) and return a 2-tuple of the Python
        representation and the index in ``s`` where the document ended.

        This can be used to decode a JSON document from a string that may
        have extraneous data at the end.

        """
        try:
            obj, end = self.scan_once(s, idx)
        except StopIteration as err:
>           raise JSONDecodeError("Expecting value", s, err.value) from None
E           json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/json/decoder.py:355: JSONDecodeError

test_algorithms.py::TestOKPAlgorithms::test_okp_to_jwk_raises_exception_on_invalid_key

test_algorithms.py::TestOKPAlgorithms::test_okp_to_jwk_raises_exception_on_invalid_key
self = 

    def test_okp_to_jwk_raises_exception_on_invalid_key(self):
        algo = OKPAlgorithm()

        with pytest.raises(InvalidKeyError):
>           algo.to_jwk({"not": "a valid key"})  # type: ignore[call-overload]

tests/test_algorithms.py:981: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

key_obj = {'not': 'a valid key'}, as_dict = False

    @staticmethod
    def to_jwk(key_obj: AllowedOKPKeys, as_dict: bool = False) -> Union[str, JWKDict]:
        if isinstance(key_obj, (Ed25519PrivateKey, Ed25519PublicKey)):
            crv = 'Ed25519'
        elif isinstance(key_obj, (Ed448PrivateKey, Ed448PublicKey)):
            crv = 'Ed448'
        else:
>           raise TypeError('Key must be an EdDSA key instance')
E           TypeError: Key must be an EdDSA key instance

jwt/algorithms.py:556: TypeError

test_algorithms.py::TestOKPAlgorithms::test_okp_ed448_jwk_fails_on_invalid_json

test_algorithms.py::TestOKPAlgorithms::test_okp_ed448_jwk_fails_on_invalid_json
self = 

    def test_okp_ed448_jwk_fails_on_invalid_json(self):
        algo = OKPAlgorithm()

        with open(key_path("jwk_okp_pub_Ed448.json")) as keyfile:
            valid_pub = json.loads(keyfile.read())
        with open(key_path("jwk_okp_key_Ed448.json")) as keyfile:
            valid_key = json.loads(keyfile.read())

        # Invalid instance type
        with pytest.raises(InvalidKeyError):
            algo.from_jwk(123)  # type: ignore[arg-type]

        # Invalid JSON
        with pytest.raises(InvalidKeyError):
>           algo.from_jwk("")

tests/test_algorithms.py:1029: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/algorithms.py:592: in from_jwk
    jwk = json.loads(jwk)
/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/json/__init__.py:346: in loads
    return _default_decoder.decode(s)
/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/json/decoder.py:337: in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
s = "", idx = 0

    def raw_decode(self, s, idx=0):
        """Decode a JSON document from ``s`` (a ``str`` beginning with
        a JSON document) and return a 2-tuple of the Python
        representation and the index in ``s`` where the document ended.

        This can be used to decode a JSON document from a string that may
        have extraneous data at the end.

        """
        try:
            obj, end = self.scan_once(s, idx)
        except StopIteration as err:
>           raise JSONDecodeError("Expecting value", s, err.value) from None
E           json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/json/decoder.py:355: JSONDecodeError

test_api_jwk.py::TestPyJWK::test_should_load_key_ec_secp256k1_from_dict

test_api_jwk.py::TestPyJWK::test_should_load_key_ec_secp256k1_from_dict
self = 

    @crypto_required
    def test_should_load_key_ec_secp256k1_from_dict(self):
        with open(key_path("jwk_ec_pub_secp256k1.json")) as keyfile:
            key_data = json.loads(keyfile.read())

>       jwk = PyJWK.from_dict(key_data)

tests/test_api_jwk.py:118: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwk.py:69: in from_dict
    return cls(obj, algorithm)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwk_data = {'crv': 'secp256k1', 'kid': 'bilbo.baggins.256k@hobbiton.example', 'kty': 'EC', 'x': 'MLnVyPDPQpNm0KaaO4iEh0i8JItHXJE0NcIe8GK1SYs', ...}
algorithm = 'ES256K'

    def __init__(self, jwk_data: JWKDict, algorithm: str | None=None) -> None:
        self._algorithms = get_default_algorithms()
        self._jwk_data = jwk_data
        kty = self._jwk_data.get('kty', None)
        if not kty:
            raise InvalidKeyError(f'kty is not found: {self._jwk_data}')
        if not algorithm and isinstance(self._jwk_data, dict):
            algorithm = self._jwk_data.get('alg', None)
        if not algorithm:
            crv = self._jwk_data.get('crv', None)
            if kty == 'EC':
                if crv == 'P-256' or not crv:
                    algorithm = 'ES256'
                elif crv == 'P-384':
                    algorithm = 'ES384'
                elif crv == 'P-521':
                    algorithm = 'ES512'
                elif crv == 'secp256k1':
                    algorithm = 'ES256K'
                else:
                    raise InvalidKeyError(f'Unsupported crv: {crv}')
            elif kty == 'RSA':
                algorithm = 'RS256'
            elif kty == 'oct':
                algorithm = 'HS256'
            elif kty == 'OKP':
                if not crv:
                    raise InvalidKeyError(f'crv is not found: {self._jwk_data}')
                if crv == 'Ed25519':
                    algorithm = 'EdDSA'
                else:
                    raise InvalidKeyError(f'Unsupported crv: {crv}')
            else:
                raise InvalidKeyError(f'Unsupported kty: {kty}')
        if not has_crypto and algorithm in requires_cryptography:
            raise PyJWKError(f"{algorithm} requires 'cryptography' to be installed.")
        self.Algorithm = self._algorithms.get(algorithm)
        if not self.Algorithm:
>           raise PyJWKError(f'Unable to find an algorithm for key: {self._jwk_data}')
E           jwt.exceptions.PyJWKError: Unable to find an algorithm for key: {'kty': 'EC', 'kid': 'bilbo.baggins.256k@hobbiton.example', 'crv': 'secp256k1', 'x': 'MLnVyPDPQpNm0KaaO4iEh0i8JItHXJE0NcIe8GK1SYs', 'y': '7r8d-xF7QAgT5kSRdly6M8xeg4Jz83Gs_CQPQRH65QI'}

jwt/api_jwk.py:49: PyJWKError

test_api_jws.py::TestJWS::test_non_object_options_dont_persist

test_api_jws.py::TestJWS::test_non_object_options_dont_persist
self = 
jws = , payload = b'hello world'

    def test_non_object_options_dont_persist(self, jws, payload):
        token = jws.encode(payload, "secret")

>       jws.decode(token, "secret", options={"verify_signature": False})

tests/test_api_jws.py:81: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jws.py:346: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, detached_payload, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ.', key = 'secret'
algorithms = ['ES384', 'RS384', 'EdDSA', 'HS256', 'RS256', 'RS512', ...]
options = {'verify_signature': False}, detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': False}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
            raise InvalidAlgorithmError('The specified alg value is not allowed')

        if alg == 'none':
            if merged_options['verify_signature']:
                raise DecodeError('Algorithm "none" not allowed')
            if key not in [None, '', 'none']:
>               raise InvalidKeyError('When alg = "none", key must be empty or "none"')
E               NameError: name 'InvalidKeyError' is not defined

jwt/api_jws.py:284: NameError

test_api_jws.py::TestJWS::test_decode_missing_segments_throws_exception

test_api_jws.py::TestJWS::test_decode_missing_segments_throws_exception
self = 
jwt = b'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9'
key = 'secret', algorithms = ['HS256'], options = {}, detached_payload = None
kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': True}
signing_input = b'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9'
crypto_segment = b'eyJoZWxsbyI6ICJ3b3JsZCJ9'

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
>           header_segment, payload_segment = signing_input.split(b'.', 1)
E           ValueError: not enough values to unpack (expected 2, got 1)

jwt/api_jws.py:234: ValueError

During handling of the above exception, another exception occurred:

self = 
jws = 

    def test_decode_missing_segments_throws_exception(self, jws):
        secret = "secret"
        example_jws = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9"  # Missing segment

        with pytest.raises(DecodeError) as context:
>           jws.decode(example_jws, secret, algorithms=["HS256"])

tests/test_api_jws.py:121: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jws.py:346: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, detached_payload, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9'
key = 'secret', algorithms = ['HS256'], options = {}, detached_payload = None
kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': True}
signing_input = b'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9'
crypto_segment = b'eyJoZWxsbyI6ICJ3b3JsZCJ9'

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
>           raise InvalidTokenError('Not enough segments')
E           jwt.exceptions.InvalidTokenError: Not enough segments

jwt/api_jws.py:236: InvalidTokenError

test_api_jws.py::TestJWS::test_encode_algorithm_param_should_be_case_sensitive

test_api_jws.py::TestJWS::test_encode_algorithm_param_should_be_case_sensitive
self = 
jws = , payload = b'hello world'

    def test_encode_algorithm_param_should_be_case_sensitive(self, jws, payload):
        jws.encode(payload, "secret", algorithm="HS256")

        with pytest.raises(NotImplementedError) as context:
>           jws.encode(payload, None, algorithm="hs256")

tests/test_api_jws.py:164: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , payload = b'hello world'
key = None, algorithm = 'hs256', headers = None, json_encoder = None
is_payload_detached = False, sort_headers = False

    def encode(self, payload: bytes, key: str | bytes | AllowedPrivateKeys | None=None, algorithm: str | None=None, headers: dict[str, Any] | None=None, json_encoder: type[json.JSONEncoder] | None=None, is_payload_detached: bool=False, sort_headers: bool=False) -> str:
        """Creates a JWT using the given algorithm.

        Args:
            payload: The claims content to sign
            key: The key to use for signing the claim. Note: if the algorithm is None, the key is not used
            algorithm: The signing algorithm to use. If none is specified then 'none' is used.
            headers: A dict of additional headers to use.
            json_encoder: A custom JSON encoder to use for encoding the JWT.
            is_payload_detached: If True, the payload will be detached from the JWS.
            sort_headers: If True, sort the header keys.
        """
        # Check that we have a mapping
        if not isinstance(payload, bytes):
            raise TypeError('Payload must be bytes')

        if algorithm is None:
            algorithm = 'none'

        if algorithm not in self._valid_algs:
>           raise InvalidAlgorithmError('Algorithm not supported')
E           jwt.exceptions.InvalidAlgorithmError: Algorithm not supported

jwt/api_jws.py:124: InvalidAlgorithmError

test_api_jws.py::TestJWS::test_encode_with_headers_alg_none

test_api_jws.py::TestJWS::test_encode_with_headers_alg_none
self = 
jws = , payload = b'hello world'

    def test_encode_with_headers_alg_none(self, jws, payload):
        msg = jws.encode(payload, key=None, headers={"alg": "none"})
        with pytest.raises(DecodeError) as context:
            jws.decode(msg, algorithms=["none"])
>       assert str(context.value) == "Signature verification failed"
E       assert 'Algorithm "none" not allowed' == 'Signature ve...cation failed'
E         
E         - Signature verification failed
E         + Algorithm "none" not allowed

tests/test_api_jws.py:173: AssertionError

test_api_jws.py::TestJWS::test_encode_with_headers_alg_es256

test_api_jws.py::TestJWS::test_encode_with_headers_alg_es256
self = 
jwt = b'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.aGVsbG8gd29ybGQ.'
key = 
algorithms = ['ES256'], options = {}, detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': True}
signing_input = b'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.aGVsbG8gd29ybGQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
            raise InvalidAlgorithmError('The specified alg value is not allowed')

        if alg == 'none':
            if merged_options['verify_signature']:
                raise DecodeError('Algorithm "none" not allowed')
            if key not in [None, '', 'none']:
                raise InvalidKeyError('When alg = "none", key must be empty or "none"')
            if signature != b'':
                raise InvalidSignatureError('Signature verification failed')
            return {
                'header': header,
                'payload': payload,
                'signature': signature
            }

        try:
            alg_obj = self._algorithms[alg]
        except KeyError:
            raise InvalidAlgorithmError('Algorithm not supported')

        if merged_options['verify_signature']:
            try:
                if key is None:
                    raise InvalidKeyError('Key is required when algorithm is not "none"')
                key = alg_obj.prepare_key(key)
            except InvalidKeyError:
                raise
            except Exception as e:
                raise InvalidTokenError('Unable to parse signature key: %s' % e)

            try:
                if not alg_obj.verify(signing_input if header.get('b64', True) else payload, key, signature):
>                   raise InvalidSignatureError('Signature verification failed')
E                   jwt.exceptions.InvalidSignatureError: Signature verification failed

jwt/api_jws.py:310: InvalidSignatureError

During handling of the above exception, another exception occurred:

self = 
jws = , payload = b'hello world'

    @crypto_required
    def test_encode_with_headers_alg_es256(self, jws, payload):
        with open(key_path("testkey_ec.priv"), "rb") as ec_priv_file:
            priv_key = load_pem_private_key(ec_priv_file.read(), password=None)
        with open(key_path("testkey_ec.pub"), "rb") as ec_pub_file:
            pub_key = load_pem_public_key(ec_pub_file.read())

        msg = jws.encode(payload, priv_key, headers={"alg": "ES256"})
>       assert b"hello world" == jws.decode(msg, pub_key, algorithms=["ES256"])

tests/test_api_jws.py:183: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jws.py:346: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, detached_payload, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.aGVsbG8gd29ybGQ.'
key = 
algorithms = ['ES256'], options = {}, detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': True}
signing_input = b'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.aGVsbG8gd29ybGQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
            raise InvalidAlgorithmError('The specified alg value is not allowed')

        if alg == 'none':
            if merged_options['verify_signature']:
                raise DecodeError('Algorithm "none" not allowed')
            if key not in [None, '', 'none']:
                raise InvalidKeyError('When alg = "none", key must be empty or "none"')
            if signature != b'':
                raise InvalidSignatureError('Signature verification failed')
            return {
                'header': header,
                'payload': payload,
                'signature': signature
            }

        try:
            alg_obj = self._algorithms[alg]
        except KeyError:
            raise InvalidAlgorithmError('Algorithm not supported')

        if merged_options['verify_signature']:
            try:
                if key is None:
                    raise InvalidKeyError('Key is required when algorithm is not "none"')
                key = alg_obj.prepare_key(key)
            except InvalidKeyError:
                raise
            except Exception as e:
                raise InvalidTokenError('Unable to parse signature key: %s' % e)

            try:
                if not alg_obj.verify(signing_input if header.get('b64', True) else payload, key, signature):
                    raise InvalidSignatureError('Signature verification failed')
            except Exception as e:
>               raise InvalidSignatureError('Signature verification failed: %s' % e)
E               jwt.exceptions.InvalidSignatureError: Signature verification failed: Signature verification failed

jwt/api_jws.py:312: InvalidSignatureError

test_api_jws.py::TestJWS::test_encode_with_alg_hs256_and_headers_alg_es256

test_api_jws.py::TestJWS::test_encode_with_alg_hs256_and_headers_alg_es256
self = , payload = b'hello world'
key = 
algorithm = 'HS256', headers = {'alg': 'ES256'}, json_encoder = None
is_payload_detached = False, sort_headers = False

    def encode(self, payload: bytes, key: str | bytes | AllowedPrivateKeys | None=None, algorithm: str | None=None, headers: dict[str, Any] | None=None, json_encoder: type[json.JSONEncoder] | None=None, is_payload_detached: bool=False, sort_headers: bool=False) -> str:
        """Creates a JWT using the given algorithm.

        Args:
            payload: The claims content to sign
            key: The key to use for signing the claim. Note: if the algorithm is None, the key is not used
            algorithm: The signing algorithm to use. If none is specified then 'none' is used.
            headers: A dict of additional headers to use.
            json_encoder: A custom JSON encoder to use for encoding the JWT.
            is_payload_detached: If True, the payload will be detached from the JWS.
            sort_headers: If True, sort the header keys.
        """
        # Check that we have a mapping
        if not isinstance(payload, bytes):
            raise TypeError('Payload must be bytes')

        if algorithm is None:
            algorithm = 'none'

        if algorithm not in self._valid_algs:
            raise InvalidAlgorithmError('Algorithm not supported')

        if algorithm != 'none' and key is None:
            raise InvalidKeyError('Key is required when algorithm is not "none"')

        # Header
        header = {'alg': algorithm}
        if self.header_typ is not None and 'typ' not in (headers or {}):
            header['typ'] = self.header_typ

        if headers:
            header.update(headers)
            if header.get('typ') == '':
                del header['typ']
            elif header.get('typ') is None:
                del header['typ']

        if is_payload_detached:
            header['b64'] = False
            if not payload:
                raise InvalidTokenError('Payload cannot be empty when using detached content')

        if sort_headers:
            header = dict(sorted(header.items()))

        json_header = json.dumps(header, separators=(',', ':'), cls=json_encoder).encode('utf-8')
        header_input = base64url_encode(json_header)

        if is_payload_detached:
            payload_input = b''
        else:
            payload_input = base64url_encode(payload)

        signing_input = b'.'.join([header_input, payload_input])

        try:
            alg_obj = self._algorithms[algorithm]
            if algorithm == 'none':
                key = None
            elif key is None:
                raise TypeError('Key is required when algorithm is not "none"')
            else:
>               key = alg_obj.prepare_key(key)

jwt/api_jws.py:166: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
key = 

    def prepare_key(self, key: Union[str, bytes]) -> bytes:
        if not isinstance(key, (str, bytes)):
>           raise TypeError('Expected a string value')
E           TypeError: Expected a string value

jwt/algorithms.py:165: TypeError

During handling of the above exception, another exception occurred:

self = 
jws = , payload = b'hello world'

    @crypto_required
    def test_encode_with_alg_hs256_and_headers_alg_es256(self, jws, payload):
        with open(key_path("testkey_ec.priv"), "rb") as ec_priv_file:
            priv_key = load_pem_private_key(ec_priv_file.read(), password=None)
        with open(key_path("testkey_ec.pub"), "rb") as ec_pub_file:
            pub_key = load_pem_public_key(ec_pub_file.read())

>       msg = jws.encode(payload, priv_key, algorithm="HS256", headers={"alg": "ES256"})

tests/test_api_jws.py:192: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , payload = b'hello world'
key = 
algorithm = 'HS256', headers = {'alg': 'ES256'}, json_encoder = None
is_payload_detached = False, sort_headers = False

    def encode(self, payload: bytes, key: str | bytes | AllowedPrivateKeys | None=None, algorithm: str | None=None, headers: dict[str, Any] | None=None, json_encoder: type[json.JSONEncoder] | None=None, is_payload_detached: bool=False, sort_headers: bool=False) -> str:
        """Creates a JWT using the given algorithm.

        Args:
            payload: The claims content to sign
            key: The key to use for signing the claim. Note: if the algorithm is None, the key is not used
            algorithm: The signing algorithm to use. If none is specified then 'none' is used.
            headers: A dict of additional headers to use.
            json_encoder: A custom JSON encoder to use for encoding the JWT.
            is_payload_detached: If True, the payload will be detached from the JWS.
            sort_headers: If True, sort the header keys.
        """
        # Check that we have a mapping
        if not isinstance(payload, bytes):
            raise TypeError('Payload must be bytes')

        if algorithm is None:
            algorithm = 'none'

        if algorithm not in self._valid_algs:
            raise InvalidAlgorithmError('Algorithm not supported')

        if algorithm != 'none' and key is None:
            raise InvalidKeyError('Key is required when algorithm is not "none"')

        # Header
        header = {'alg': algorithm}
        if self.header_typ is not None and 'typ' not in (headers or {}):
            header['typ'] = self.header_typ

        if headers:
            header.update(headers)
            if header.get('typ') == '':
                del header['typ']
            elif header.get('typ') is None:
                del header['typ']

        if is_payload_detached:
            header['b64'] = False
            if not payload:
                raise InvalidTokenError('Payload cannot be empty when using detached content')

        if sort_headers:
            header = dict(sorted(header.items()))

        json_header = json.dumps(header, separators=(',', ':'), cls=json_encoder).encode('utf-8')
        header_input = base64url_encode(json_header)

        if is_payload_detached:
            payload_input = b''
        else:
            payload_input = base64url_encode(payload)

        signing_input = b'.'.join([header_input, payload_input])

        try:
            alg_obj = self._algorithms[algorithm]
            if algorithm == 'none':
                key = None
            elif key is None:
                raise TypeError('Key is required when algorithm is not "none"')
            else:
                key = alg_obj.prepare_key(key)
            signature = alg_obj.sign(signing_input if not is_payload_detached else payload, key)
        except Exception as e:
>           raise TypeError('Unable to encode JWT: %s' % e)
E           TypeError: Unable to encode JWT: Expected a string value

jwt/api_jws.py:169: TypeError

test_api_jws.py::TestJWS::test_bad_secret

test_api_jws.py::TestJWS::test_bad_secret
self = 
jws = , payload = b'hello world'

    def test_bad_secret(self, jws, payload):
        right_secret = "foo"
        bad_secret = "bar"
        jws_message = jws.encode(payload, right_secret)

        with pytest.raises(DecodeError) as excinfo:
            # Backward compat for ticket #315
>           jws.decode(jws_message, bad_secret, algorithms=["HS256"])

tests/test_api_jws.py:215: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jws.py:346: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, detached_payload, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ.', key = 'bar'
algorithms = ['HS256'], options = {}, detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': True}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jws.py::TestJWS::test_decode_with_optional_algorithms

test_api_jws.py::TestJWS::test_decode_with_optional_algorithms
self = 
jws = 

    def test_decode_with_optional_algorithms(self, jws):
        example_secret = "secret"
        example_jws = (
            b"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
            b"aGVsbG8gd29ybGQ."
            b"SIr03zM64awWRdPrAM_61QWsZchAtgDV3pphfHPPWkI"
        )

>       with pytest.raises(DecodeError) as exc:
E       Failed: DID NOT RAISE 

tests/test_api_jws.py:326: Failed

test_api_jws.py::TestJWS::test_load_no_verification

test_api_jws.py::TestJWS::test_load_no_verification
self = 
jws = , payload = b'hello world'

    def test_load_no_verification(self, jws, payload):
        right_secret = "foo"
        jws_message = jws.encode(payload, right_secret)

>       decoded_payload = jws.decode(
            jws_message,
            key=None,
            algorithms=["HS256"],
            options={"verify_signature": False},
        )

tests/test_api_jws.py:352: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jws.py:346: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, detached_payload, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ.', key = None
algorithms = ['HS256'], options = {'verify_signature': False}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': False}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jws.py::TestJWS::test_no_secret

test_api_jws.py::TestJWS::test_no_secret
self = 
jws = , payload = b'hello world'

    def test_no_secret(self, jws, payload):
        right_secret = "foo"
        jws_message = jws.encode(payload, right_secret)

        with pytest.raises(DecodeError):
>           jws.decode(jws_message, algorithms=["HS256"])

tests/test_api_jws.py:366: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jws.py:346: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, detached_payload, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ.', key = None
algorithms = ['HS256'], options = {}, detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': True}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jws.py::TestJWS::test_verify_signature_with_no_secret

test_api_jws.py::TestJWS::test_verify_signature_with_no_secret
self = 
jws = , payload = b'hello world'

    def test_verify_signature_with_no_secret(self, jws, payload):
        right_secret = "foo"
        jws_message = jws.encode(payload, right_secret)

        with pytest.raises(DecodeError) as exc:
>           jws.decode(jws_message, algorithms=["HS256"])

tests/test_api_jws.py:373: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jws.py:346: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, detached_payload, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ.', key = None
algorithms = ['HS256'], options = {}, detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': True}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jws.py::TestJWS::test_verify_signature_with_no_algo_header_throws_exception

test_api_jws.py::TestJWS::test_verify_signature_with_no_algo_header_throws_exception
self = 
jwt = b'e30.eyJhIjo1fQ.KEh186CjVw_Q8FadjJcaVnE7hO5Z9nHBbU8TgbhHcBY'
key = 'secret', algorithms = ['HS256'], options = {}, detached_payload = None
kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': True}, signing_input = b'e30.eyJhIjo1fQ'
crypto_segment = b'KEh186CjVw_Q8FadjJcaVnE7hO5Z9nHBbU8TgbhHcBY'

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
>           alg = header['alg']
E           KeyError: 'alg'

jwt/api_jws.py:273: KeyError

During handling of the above exception, another exception occurred:

self = 
jws = , payload = b'hello world'

    def test_verify_signature_with_no_algo_header_throws_exception(self, jws, payload):
        example_jws = b"e30.eyJhIjo1fQ.KEh186CjVw_Q8FadjJcaVnE7hO5Z9nHBbU8TgbhHcBY"

        with pytest.raises(InvalidAlgorithmError):
>           jws.decode(example_jws, "secret", algorithms=["HS256"])

tests/test_api_jws.py:381: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jws.py:346: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, detached_payload, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'e30.eyJhIjo1fQ.KEh186CjVw_Q8FadjJcaVnE7hO5Z9nHBbU8TgbhHcBY'
key = 'secret', algorithms = ['HS256'], options = {}, detached_payload = None
kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': True}, signing_input = b'e30.eyJhIjo1fQ'
crypto_segment = b'KEh186CjVw_Q8FadjJcaVnE7hO5Z9nHBbU8TgbhHcBY'

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
>           raise InvalidTokenError('Missing algorithm ("alg") in headers')
E           jwt.exceptions.InvalidTokenError: Missing algorithm ("alg") in headers

jwt/api_jws.py:275: InvalidTokenError

test_api_jws.py::TestJWS::test_invalid_crypto_alg

test_api_jws.py::TestJWS::test_invalid_crypto_alg
self = 
jws = , payload = b'hello world'

    def test_invalid_crypto_alg(self, jws, payload):
        with pytest.raises(NotImplementedError):
>           jws.encode(payload, "secret", algorithm="HS1024")

tests/test_api_jws.py:385: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , payload = b'hello world'
key = 'secret', algorithm = 'HS1024', headers = None, json_encoder = None
is_payload_detached = False, sort_headers = False

    def encode(self, payload: bytes, key: str | bytes | AllowedPrivateKeys | None=None, algorithm: str | None=None, headers: dict[str, Any] | None=None, json_encoder: type[json.JSONEncoder] | None=None, is_payload_detached: bool=False, sort_headers: bool=False) -> str:
        """Creates a JWT using the given algorithm.

        Args:
            payload: The claims content to sign
            key: The key to use for signing the claim. Note: if the algorithm is None, the key is not used
            algorithm: The signing algorithm to use. If none is specified then 'none' is used.
            headers: A dict of additional headers to use.
            json_encoder: A custom JSON encoder to use for encoding the JWT.
            is_payload_detached: If True, the payload will be detached from the JWS.
            sort_headers: If True, sort the header keys.
        """
        # Check that we have a mapping
        if not isinstance(payload, bytes):
            raise TypeError('Payload must be bytes')

        if algorithm is None:
            algorithm = 'none'

        if algorithm not in self._valid_algs:
>           raise InvalidAlgorithmError('Algorithm not supported')
E           jwt.exceptions.InvalidAlgorithmError: Algorithm not supported

jwt/api_jws.py:124: InvalidAlgorithmError

test_api_jws.py::TestJWS::test_unicode_secret

test_api_jws.py::TestJWS::test_unicode_secret
self = 
jws = , payload = b'hello world'

    def test_unicode_secret(self, jws, payload):
        secret = "\xc2"
        jws_message = jws.encode(payload, secret)
>       decoded_payload = jws.decode(jws_message, secret, algorithms=["HS256"])

tests/test_api_jws.py:396: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jws.py:346: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, detached_payload, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ.', key = 'Â'
algorithms = ['HS256'], options = {}, detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': True}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jws.py::TestJWS::test_nonascii_secret

test_api_jws.py::TestJWS::test_nonascii_secret
self = 
jws = , payload = b'hello world'

    def test_nonascii_secret(self, jws, payload):
        secret = "\xc2"  # char value that ascii codec cannot decode
        jws_message = jws.encode(payload, secret)

>       decoded_payload = jws.decode(jws_message, secret, algorithms=["HS256"])

tests/test_api_jws.py:404: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jws.py:346: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, detached_payload, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ.', key = 'Â'
algorithms = ['HS256'], options = {}, detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': True}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jws.py::TestJWS::test_bytes_secret

test_api_jws.py::TestJWS::test_bytes_secret
self = 
jws = , payload = b'hello world'

    def test_bytes_secret(self, jws, payload):
        secret = b"\xc2"  # char value that ascii codec cannot decode
        jws_message = jws.encode(payload, secret)

>       decoded_payload = jws.decode(jws_message, secret, algorithms=["HS256"])

tests/test_api_jws.py:412: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jws.py:346: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, detached_payload, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ.', key = b'\xc2'
algorithms = ['HS256'], options = {}, detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'verify_signature': True}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.aGVsbG8gd29ybGQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jws.py::TestJWS::test_get_unverified_header_fails_on_bad_header_types

test_api_jws.py::TestJWS::test_get_unverified_header_fails_on_bad_header_types
self = 
jws = , payload = b'hello world'

    def test_get_unverified_header_fails_on_bad_header_types(self, jws, payload):
        # Contains a bad kid value (int 123 instead of string)
        example_jws = (
            "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6MTIzfQ"
            ".eyJzdWIiOiIxMjM0NTY3ODkwIn0"
            ".vs2WY54jfpKP3JGC73Vq5YlMsqM5oTZ1ZydT77SiZSk"
        )

>       with pytest.raises(InvalidTokenError) as exc:
E       Failed: DID NOT RAISE 

tests/test_api_jws.py:510: Failed
test_api_jws.py::TestJWS::test_encode_decode_ecdsa_related_algorithms[ES256K]
self = 
jws = , payload = b'hello world'
algo = 'ES256K'

    @pytest.mark.parametrize(
        "algo",
        [
            "ES256",
            "ES256K",
            "ES384",
            "ES512",
        ],
    )
    @crypto_required
    def test_encode_decode_ecdsa_related_algorithms(self, jws, payload, algo):
        # PEM-formatted EC key
        with open(key_path("testkey_ec.priv"), "rb") as ec_priv_file:
            priv_eckey = load_pem_private_key(ec_priv_file.read(), password=None)
>           jws_message = jws.encode(payload, priv_eckey, algorithm=algo)

tests/test_api_jws.py:577: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , payload = b'hello world'
key = 
algorithm = 'ES256K', headers = None, json_encoder = None
is_payload_detached = False, sort_headers = False

    def encode(self, payload: bytes, key: str | bytes | AllowedPrivateKeys | None=None, algorithm: str | None=None, headers: dict[str, Any] | None=None, json_encoder: type[json.JSONEncoder] | None=None, is_payload_detached: bool=False, sort_headers: bool=False) -> str:
        """Creates a JWT using the given algorithm.

        Args:
            payload: The claims content to sign
            key: The key to use for signing the claim. Note: if the algorithm is None, the key is not used
            algorithm: The signing algorithm to use. If none is specified then 'none' is used.
            headers: A dict of additional headers to use.
            json_encoder: A custom JSON encoder to use for encoding the JWT.
            is_payload_detached: If True, the payload will be detached from the JWS.
            sort_headers: If True, sort the header keys.
        """
        # Check that we have a mapping
        if not isinstance(payload, bytes):
            raise TypeError('Payload must be bytes')

        if algorithm is None:
            algorithm = 'none'

        if algorithm not in self._valid_algs:
>           raise InvalidAlgorithmError('Algorithm not supported')
E           jwt.exceptions.InvalidAlgorithmError: Algorithm not supported

jwt/api_jws.py:124: InvalidAlgorithmError
test_api_jws.py::TestJWS::test_ecdsa_related_algorithms
self = 
jws = 

    def test_ecdsa_related_algorithms(self, jws):
        jws = PyJWS()
        jws_algorithms = jws.get_algorithms()

        if has_crypto:
            assert "ES256" in jws_algorithms
>           assert "ES256K" in jws_algorithms
E           AssertionError: assert 'ES256K' in ['ES384', 'RS384', 'EdDSA', 'HS256', 'RS256', 'RS512', ...]

tests/test_api_jws.py:598: AssertionError

test_api_jws.py::TestJWS::test_encode_fails_on_invalid_kid_types

test_api_jws.py::TestJWS::test_encode_fails_on_invalid_kid_types
self = 
jws = , payload = b'hello world'

    def test_encode_fails_on_invalid_kid_types(self, jws, payload):
>       with pytest.raises(InvalidTokenError) as exc:
E       Failed: DID NOT RAISE 

tests/test_api_jws.py:724: Failed

test_api_jws.py::TestJWS::test_encode_detached_content_with_b64_header

test_api_jws.py::TestJWS::test_encode_detached_content_with_b64_header
self = 
jws = , payload = b'hello world'

    def test_encode_detached_content_with_b64_header(self, jws, payload):
        secret = "secret"

        # Check that detached content is automatically detected when b64 is false
        headers = {"b64": False}
        token = jws.encode(payload, secret, "HS256", headers)

        msg_header, msg_payload, _ = token.split(".")
        msg_header = base64url_decode(msg_header.encode())
        msg_header_obj = json.loads(msg_header)

        assert "b64" in msg_header_obj
        assert msg_header_obj["b64"] is False
        # Check that the payload is not inside the token
>       assert not msg_payload
E       AssertionError: assert not 'aGVsbG8gd29ybGQ'

tests/test_api_jws.py:756: AssertionError

test_api_jws.py::TestJWS::test_decode_detached_content_without_proper_argument

test_api_jws.py::TestJWS::test_decode_detached_content_without_proper_argument
self = 
jws = 

    def test_decode_detached_content_without_proper_argument(self, jws):
        example_jws = (
            "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2V9"
            "."
            ".65yNkX_ZH4A_6pHaTL_eI84OXOHtfl4K0k5UnlXZ8f4"
        )
        example_secret = "secret"

        with pytest.raises(DecodeError) as exc:
            jws.decode(example_jws, example_secret, algorithms=["HS256"])

>       assert (
            'It is required that you pass in a value for the "detached_payload" argument to decode a message having the b64 header set to false.'
            in str(exc.value)
        )
E       assert 'It is required that you pass in a value for the "detached_payload" argument to decode a message having the b64 header set to false.' in 'It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.'
E        +  where 'It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.' = str(DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.'))
E        +    where DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.') = .value

tests/test_api_jws.py:780: AssertionError

test_api_jwt.py::TestJWT::test_decode_with_invalid_audience_param_throws_exception

test_api_jwt.py::TestJWT::test_decode_with_invalid_audience_param_throws_exception
self = 
jwt = 

    def test_decode_with_invalid_audience_param_throws_exception(self, jwt):
        secret = "secret"
        example_jwt = (
            "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9"
            ".eyJoZWxsbyI6ICJ3b3JsZCJ9"
            ".tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8"
        )

        with pytest.raises(TypeError) as context:
>           jwt.decode(example_jwt, secret, audience=1, algorithms=["HS256"])

tests/test_api_jwt.py:119: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8'
key = 'secret', algorithms = ['HS256'], options = None, kwargs = {'audience': 1}
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
decoded = {'header': {'alg': 'HS256', 'typ': 'JWT'}, 'payload': b'{"hello": "world"}', 'signature': b'\xb6\xf6\xa0,2\xe8j"J\xc4\xe2\xaa\xa4\x15\xd2\x10l\xbbI\x84\xa2}\x98c\x9e\xd8&\xf5\xcbi\xca?'}
payload = {'hello': 'world'}

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, **kwargs: Any) -> dict[str, Any]:
        """
        Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        merged_options = {**self.options, **(options or {})}
        decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
        payload = self._decode_payload(decoded)

        if merged_options['verify_exp'] and 'exp' in payload:
            now = kwargs.get('now', datetime.now(timezone.utc))
            exp = datetime.fromtimestamp(payload['exp'], tz=timezone.utc)
            leeway = timedelta(seconds=kwargs.get('leeway', 0))
            if now > exp + leeway:
                raise ExpiredSignatureError('Signature has expired')

        if merged_options['verify_nbf'] and 'nbf' in payload:
            now = kwargs.get('now', datetime.now(timezone.utc))
            nbf = datetime.fromtimestamp(payload['nbf'], tz=timezone.utc)
            leeway = timedelta(seconds=kwargs.get('leeway', 0))
            if now < nbf - leeway:
                raise ImmatureSignatureError('The token is not yet valid (nbf)')

        if merged_options['verify_iat'] and 'iat' in payload:
            now = kwargs.get('now', datetime.now(timezone.utc))
            iat = datetime.fromtimestamp(payload['iat'], tz=timezone.utc)
            leeway = timedelta(seconds=kwargs.get('leeway', 0))
            if now < iat - leeway:
                raise InvalidIssuedAtError('Issued at claim (iat) cannot be in the future')

        if merged_options['verify_iss']:
            expected_issuer = kwargs.get('issuer', None)
            if expected_issuer is not None:
                if 'iss' not in payload:
                    raise MissingRequiredClaimError('Issuer claim expected but not present')
                if payload['iss'] != expected_issuer:
                    raise InvalidIssuerError('Invalid issuer')

        if merged_options['verify_aud']:
            expected_audience = kwargs.get('audience', None)
            if expected_audience is not None:
                if 'aud' not in payload:
>                   raise MissingRequiredClaimError('Audience claim expected but not present')
E                   jwt.exceptions.MissingRequiredClaimError: Token is missing the "Audience claim expected but not present" claim

jwt/api_jwt.py:147: MissingRequiredClaimError

test_api_jwt.py::TestJWT::test_decode_raises_exception_if_exp_is_not_int

test_api_jwt.py::TestJWT::test_decode_raises_exception_if_exp_is_not_int
self = 
jwt = 

    def test_decode_raises_exception_if_exp_is_not_int(self, jwt):
        # >>> jwt.encode({'exp': 'not-an-int'}, 'secret')
        example_jwt = (
            "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
            "eyJleHAiOiJub3QtYW4taW50In0."
            "P65iYgoHtBqB07PMtBSuKNUEIPPPfmjfJG217cEE66s"
        )

        with pytest.raises(DecodeError) as exc:
>           jwt.decode(example_jwt, "secret", algorithms=["HS256"])

tests/test_api_jwt.py:206: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiJub3QtYW4taW50In0.P65iYgoHtBqB07PMtBSuKNUEIPPPfmjfJG217cEE66s'
key = 'secret', algorithms = ['HS256'], options = None, kwargs = {}
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
decoded = {'header': {'alg': 'HS256', 'typ': 'JWT'}, 'payload': b'{"exp":"not-an-int"}', 'signature': b'?\xaebb\n\x07\xb4\x1a\x81\xd3\xb3\xcc\xb4\x14\xae(\xd5\x04 \xf3\xcf~h\xdf$m\xb5\xed\xc1\x04\xeb\xab'}
payload = {'exp': 'not-an-int'}
now = datetime.datetime(2024, 11, 29, 4, 31, 42, 942554, tzinfo=datetime.timezone.utc)

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, **kwargs: Any) -> dict[str, Any]:
        """
        Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        merged_options = {**self.options, **(options or {})}
        decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
        payload = self._decode_payload(decoded)

        if merged_options['verify_exp'] and 'exp' in payload:
            now = kwargs.get('now', datetime.now(timezone.utc))
>           exp = datetime.fromtimestamp(payload['exp'], tz=timezone.utc)
E           TypeError: 'str' object cannot be interpreted as an integer

jwt/api_jwt.py:116: TypeError

test_api_jwt.py::TestJWT::test_decode_raises_exception_if_iat_is_not_int

test_api_jwt.py::TestJWT::test_decode_raises_exception_if_iat_is_not_int
self = 
jwt = 

    def test_decode_raises_exception_if_iat_is_not_int(self, jwt):
        # >>> jwt.encode({'iat': 'not-an-int'}, 'secret')
        example_jwt = (
            "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
            "eyJpYXQiOiJub3QtYW4taW50In0."
            "H1GmcQgSySa5LOKYbzGm--b1OmRbHFkyk8pq811FzZM"
        )

        with pytest.raises(InvalidIssuedAtError):
>           jwt.decode(example_jwt, "secret", algorithms=["HS256"])

tests/test_api_jwt.py:219: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiJub3QtYW4taW50In0.H1GmcQgSySa5LOKYbzGm--b1OmRbHFkyk8pq811FzZM'
key = 'secret', algorithms = ['HS256'], options = None, kwargs = {}
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
decoded = {'header': {'alg': 'HS256', 'typ': 'JWT'}, 'payload': b'{"iat":"not-an-int"}', 'signature': b'\x1fQ\xa6q\x08\x12\xc9&\xb9,\xe2\x98o1\xa6\xfb\xe6\xf5:d[\x1cY2\x93\xcaj\xf3]E\xcd\x93'}
payload = {'iat': 'not-an-int'}
now = datetime.datetime(2024, 11, 29, 4, 31, 42, 964811, tzinfo=datetime.timezone.utc)

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, **kwargs: Any) -> dict[str, Any]:
        """
        Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        merged_options = {**self.options, **(options or {})}
        decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
        payload = self._decode_payload(decoded)

        if merged_options['verify_exp'] and 'exp' in payload:
            now = kwargs.get('now', datetime.now(timezone.utc))
            exp = datetime.fromtimestamp(payload['exp'], tz=timezone.utc)
            leeway = timedelta(seconds=kwargs.get('leeway', 0))
            if now > exp + leeway:
                raise ExpiredSignatureError('Signature has expired')

        if merged_options['verify_nbf'] and 'nbf' in payload:
            now = kwargs.get('now', datetime.now(timezone.utc))
            nbf = datetime.fromtimestamp(payload['nbf'], tz=timezone.utc)
            leeway = timedelta(seconds=kwargs.get('leeway', 0))
            if now < nbf - leeway:
                raise ImmatureSignatureError('The token is not yet valid (nbf)')

        if merged_options['verify_iat'] and 'iat' in payload:
            now = kwargs.get('now', datetime.now(timezone.utc))
>           iat = datetime.fromtimestamp(payload['iat'], tz=timezone.utc)
E           TypeError: 'str' object cannot be interpreted as an integer

jwt/api_jwt.py:130: TypeError

test_api_jwt.py::TestJWT::test_decode_raises_exception_if_iat_is_greater_than_now

test_api_jwt.py::TestJWT::test_decode_raises_exception_if_iat_is_greater_than_now
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854717, 'iat': 1732854712, 'iss': 'jeff'}

    def test_decode_raises_exception_if_iat_is_greater_than_now(self, jwt, payload):
        payload["iat"] = utc_timestamp() + 10
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

        with pytest.raises(ImmatureSignatureError):
>           jwt.decode(jwt_message, secret, algorithms=["HS256"])

tests/test_api_jwt.py:227: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE3LCJjbGFpbSI6Imluc2FuaXR5IiwiaWF0IjoxNzMyODU0NzEyfQ.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE3LCJjbGFpbSI6Imluc2FuaXR5IiwiaWF0IjoxNzMyODU0NzEyfQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_works_if_iat_is_str_of_a_number

test_api_jwt.py::TestJWT::test_decode_works_if_iat_is_str_of_a_number
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854718, 'iat': 1638202770, 'iss': 'jeff'}

    def test_decode_works_if_iat_is_str_of_a_number(self, jwt, payload):
        payload["iat"] = "1638202770"
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)
>       data = jwt.decode(jwt_message, secret, algorithms=["HS256"])

tests/test_api_jwt.py:233: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5IiwiaWF0IjoxNjM4MjAyNzcwfQ.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5IiwiaWF0IjoxNjM4MjAyNzcwfQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_raises_exception_if_nbf_is_not_int

test_api_jwt.py::TestJWT::test_decode_raises_exception_if_nbf_is_not_int
self = 
jwt = 

    def test_decode_raises_exception_if_nbf_is_not_int(self, jwt):
        # >>> jwt.encode({'nbf': 'not-an-int'}, 'secret')
        example_jwt = (
            "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
            "eyJuYmYiOiJub3QtYW4taW50In0."
            "c25hldC8G2ZamC8uKpax9sYMTgdZo3cxrmzFHaAAluw"
        )

        with pytest.raises(DecodeError):
>           jwt.decode(example_jwt, "secret", algorithms=["HS256"])

tests/test_api_jwt.py:245: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOiJub3QtYW4taW50In0.c25hldC8G2ZamC8uKpax9sYMTgdZo3cxrmzFHaAAluw'
key = 'secret', algorithms = ['HS256'], options = None, kwargs = {}
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
decoded = {'header': {'alg': 'HS256', 'typ': 'JWT'}, 'payload': b'{"nbf":"not-an-int"}', 'signature': b'sna\x95\xd0\xbc\x1bfZ\x98/.*\x96\xb1\xf6\xc6\x0cN\x07Y\xa3w1\xael\xc5\x1d\xa0\x00\x96\xec'}
payload = {'nbf': 'not-an-int'}
now = datetime.datetime(2024, 11, 29, 4, 31, 43, 36709, tzinfo=datetime.timezone.utc)

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, **kwargs: Any) -> dict[str, Any]:
        """
        Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        merged_options = {**self.options, **(options or {})}
        decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
        payload = self._decode_payload(decoded)

        if merged_options['verify_exp'] and 'exp' in payload:
            now = kwargs.get('now', datetime.now(timezone.utc))
            exp = datetime.fromtimestamp(payload['exp'], tz=timezone.utc)
            leeway = timedelta(seconds=kwargs.get('leeway', 0))
            if now > exp + leeway:
                raise ExpiredSignatureError('Signature has expired')

        if merged_options['verify_nbf'] and 'nbf' in payload:
            now = kwargs.get('now', datetime.now(timezone.utc))
>           nbf = datetime.fromtimestamp(payload['nbf'], tz=timezone.utc)
E           TypeError: 'str' object cannot be interpreted as an integer

jwt/api_jwt.py:123: TypeError

test_api_jwt.py::TestJWT::test_encode_datetime

test_api_jwt.py::TestJWT::test_encode_datetime
self = 
jwt = 

    def test_encode_datetime(self, jwt):
        secret = "secret"
        current_datetime = datetime.now(tz=timezone.utc)
        payload = {
            "exp": current_datetime,
            "iat": current_datetime,
            "nbf": current_datetime,
        }
        jwt_message = jwt.encode(payload, secret)
>       decoded_payload = jwt.decode(
            jwt_message, secret, leeway=1, algorithms=["HS256"]
        )

tests/test_api_jwt.py:266: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOjE3MzI4NTQ3MDMsImlhdCI6MTczMjg1NDcwMywibmJmIjoxNzMyODU0NzAzfQ.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOjE3MzI4NTQ3MDMsImlhdCI6MTczMjg1NDcwMywibmJmIjoxNzMyODU0NzAzfQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_with_expiration

test_api_jwt.py::TestJWT::test_decode_with_expiration
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854702, 'iss': 'jeff'}

    def test_decode_with_expiration(self, jwt, payload):
        payload["exp"] = utc_timestamp() - 1
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

        with pytest.raises(ExpiredSignatureError):
>           jwt.decode(jwt_message, secret, algorithms=["HS256"])

tests/test_api_jwt.py:329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzAyLCJjbGFpbSI6Imluc2FuaXR5In0.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzAyLCJjbGFpbSI6Imluc2FuaXR5In0'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_with_notbefore

test_api_jwt.py::TestJWT::test_decode_with_notbefore
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854718, 'iss': 'jeff', 'nbf': 1732854713}

    def test_decode_with_notbefore(self, jwt, payload):
        payload["nbf"] = utc_timestamp() + 10
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

        with pytest.raises(ImmatureSignatureError):
>           jwt.decode(jwt_message, secret, algorithms=["HS256"])

tests/test_api_jwt.py:337: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5IiwibmJmIjoxNzMyODU0NzEzfQ.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5IiwibmJmIjoxNzMyODU0NzEzfQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_skip_expiration_verification

test_api_jwt.py::TestJWT::test_decode_skip_expiration_verification
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854702.144174, 'iss': 'jeff'}

    def test_decode_skip_expiration_verification(self, jwt, payload):
        payload["exp"] = time.time() - 1
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

>       jwt.decode(
            jwt_message,
            secret,
            algorithms=["HS256"],
            options={"verify_exp": False},
        )

tests/test_api_jwt.py:344: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzAyLjE0NDE3NCwiY2xhaW0iOiJpbnNhbml0eSJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': False, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': False, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzAyLjE0NDE3NCwiY2xhaW0iOiJpbnNhbml0eSJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_skip_notbefore_verification

test_api_jwt.py::TestJWT::test_decode_skip_notbefore_verification
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854718, 'iss': 'jeff', 'nbf': 1732854713.169743}

    def test_decode_skip_notbefore_verification(self, jwt, payload):
        payload["nbf"] = time.time() + 10
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

>       jwt.decode(
            jwt_message,
            secret,
            algorithms=["HS256"],
            options={"verify_nbf": False},
        )

tests/test_api_jwt.py:356: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5IiwibmJmIjoxNzMyODU0NzEzLjE2OTc0M30.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5IiwibmJmIjoxNzMyODU0NzEzLjE2OTc0M30'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_with_expiration_with_leeway

test_api_jwt.py::TestJWT::test_decode_with_expiration_with_leeway
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854701, 'iss': 'jeff'}

    def test_decode_with_expiration_with_leeway(self, jwt, payload):
        payload["exp"] = utc_timestamp() - 2
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

        # With 5 seconds leeway, should be ok
        for leeway in (5, timedelta(seconds=5)):
>           decoded = jwt.decode(
                jwt_message, secret, leeway=leeway, algorithms=["HS256"]
            )

tests/test_api_jwt.py:370: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzAxLCJjbGFpbSI6Imluc2FuaXR5In0.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzAxLCJjbGFpbSI6Imluc2FuaXR5In0'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_with_notbefore_with_leeway

test_api_jwt.py::TestJWT::test_decode_with_notbefore_with_leeway
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854718, 'iss': 'jeff', 'nbf': 1732854713}

    def test_decode_with_notbefore_with_leeway(self, jwt, payload):
        payload["nbf"] = utc_timestamp() + 10
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

        # With 13 seconds leeway, should be ok
>       jwt.decode(jwt_message, secret, leeway=13, algorithms=["HS256"])

tests/test_api_jwt.py:386: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5IiwibmJmIjoxNzMyODU0NzEzfQ.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5IiwibmJmIjoxNzMyODU0NzEzfQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_check_audience_when_valid

test_api_jwt.py::TestJWT::test_check_audience_when_valid
self = 
jwt = 

    def test_check_audience_when_valid(self, jwt):
        payload = {"some": "payload", "aud": "urn:me"}
        token = jwt.encode(payload, "secret")
>       jwt.decode(token, "secret", audience="urn:me", algorithms=["HS256"])

tests/test_api_jwt.py:394: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6InVybjptZSJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6InVybjptZSJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_check_audience_list_when_valid

test_api_jwt.py::TestJWT::test_check_audience_list_when_valid
self = 
jwt = 

    def test_check_audience_list_when_valid(self, jwt):
        payload = {"some": "payload", "aud": "urn:me"}
        token = jwt.encode(payload, "secret")
>       jwt.decode(
            token,
            "secret",
            audience=["urn:you", "urn:me"],
            algorithms=["HS256"],
        )

tests/test_api_jwt.py:399: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6InVybjptZSJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6InVybjptZSJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_check_audience_none_specified

test_api_jwt.py::TestJWT::test_check_audience_none_specified
self = 
jwt = 

    def test_check_audience_none_specified(self, jwt):
        payload = {"some": "payload", "aud": "urn:me"}
        token = jwt.encode(payload, "secret")
        with pytest.raises(InvalidAudienceError):
>           jwt.decode(token, "secret", algorithms=["HS256"])

tests/test_api_jwt.py:410: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6InVybjptZSJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6InVybjptZSJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_raise_exception_invalid_audience_list

test_api_jwt.py::TestJWT::test_raise_exception_invalid_audience_list
self = 
jwt = 

    def test_raise_exception_invalid_audience_list(self, jwt):
        payload = {"some": "payload", "aud": "urn:me"}
        token = jwt.encode(payload, "secret")
        with pytest.raises(InvalidAudienceError):
>           jwt.decode(
                token,
                "secret",
                audience=["urn:you", "urn:him"],
                algorithms=["HS256"],
            )

tests/test_api_jwt.py:416: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6InVybjptZSJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6InVybjptZSJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_check_audience_in_array_when_valid

test_api_jwt.py::TestJWT::test_check_audience_in_array_when_valid
self = 
jwt = 

    def test_check_audience_in_array_when_valid(self, jwt):
        payload = {"some": "payload", "aud": ["urn:me", "urn:someone-else"]}
        token = jwt.encode(payload, "secret")
>       jwt.decode(token, "secret", audience="urn:me", algorithms=["HS256"])

tests/test_api_jwt.py:426: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6WyJ1cm46bWUiLCJ1cm46c29tZW9uZS1lbHNlIl19.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6WyJ1cm46bWUiLCJ1cm46c29tZW9uZS1lbHNlIl19'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_raise_exception_invalid_audience

test_api_jwt.py::TestJWT::test_raise_exception_invalid_audience
self = 
jwt = 

    def test_raise_exception_invalid_audience(self, jwt):
        payload = {"some": "payload", "aud": "urn:someone-else"}

        token = jwt.encode(payload, "secret")

        with pytest.raises(InvalidAudienceError):
>           jwt.decode(token, "secret", audience="urn-me", algorithms=["HS256"])

tests/test_api_jwt.py:434: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6InVybjpzb21lb25lLWVsc2UifQ.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6InVybjpzb21lb25lLWVsc2UifQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_raise_exception_audience_as_bytes

test_api_jwt.py::TestJWT::test_raise_exception_audience_as_bytes
self = 
jwt = 

    def test_raise_exception_audience_as_bytes(self, jwt):
        payload = {"some": "payload", "aud": ["urn:me", "urn:someone-else"]}
        token = jwt.encode(payload, "secret")
        with pytest.raises(InvalidAudienceError):
>           jwt.decode(
                token, "secret", audience="urn:me".encode(), algorithms=["HS256"]
            )

tests/test_api_jwt.py:440: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6WyJ1cm46bWUiLCJ1cm46c29tZW9uZS1lbHNlIl19.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6WyJ1cm46bWUiLCJ1cm46c29tZW9uZS1lbHNlIl19'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_raise_exception_invalid_audience_in_array

test_api_jwt.py::TestJWT::test_raise_exception_invalid_audience_in_array
self = 
jwt = 

    def test_raise_exception_invalid_audience_in_array(self, jwt):
        payload = {
            "some": "payload",
            "aud": ["urn:someone", "urn:someone-else"],
        }

        token = jwt.encode(payload, "secret")

        with pytest.raises(InvalidAudienceError):
>           jwt.decode(token, "secret", audience="urn:me", algorithms=["HS256"])

tests/test_api_jwt.py:453: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6WyJ1cm46c29tZW9uZSIsInVybjpzb21lb25lLWVsc2UiXX0.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6WyJ1cm46c29tZW9uZSIsInVybjpzb21lb25lLWVsc2UiXX0'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_raise_exception_token_without_issuer

test_api_jwt.py::TestJWT::test_raise_exception_token_without_issuer
self = 
jwt = 

    def test_raise_exception_token_without_issuer(self, jwt):
        issuer = "urn:wrong"

        payload = {"some": "payload"}

        token = jwt.encode(payload, "secret")

        with pytest.raises(MissingRequiredClaimError) as exc:
>           jwt.decode(token, "secret", issuer=issuer, algorithms=["HS256"])

tests/test_api_jwt.py:463: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_raise_exception_token_without_audience

test_api_jwt.py::TestJWT::test_raise_exception_token_without_audience
self = 
jwt = 

    def test_raise_exception_token_without_audience(self, jwt):
        payload = {"some": "payload"}
        token = jwt.encode(payload, "secret")

        with pytest.raises(MissingRequiredClaimError) as exc:
>           jwt.decode(token, "secret", audience="urn:me", algorithms=["HS256"])

tests/test_api_jwt.py:472: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_raise_exception_token_with_aud_none_and_without_audience

test_api_jwt.py::TestJWT::test_raise_exception_token_with_aud_none_and_without_audience
self = 
jwt = 

    def test_raise_exception_token_with_aud_none_and_without_audience(self, jwt):
        payload = {"some": "payload", "aud": None}
        token = jwt.encode(payload, "secret")

        with pytest.raises(MissingRequiredClaimError) as exc:
>           jwt.decode(token, "secret", audience="urn:me", algorithms=["HS256"])

tests/test_api_jwt.py:481: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6bnVsbH0.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6bnVsbH0'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_check_issuer_when_valid

test_api_jwt.py::TestJWT::test_check_issuer_when_valid
self = 
jwt = 

    def test_check_issuer_when_valid(self, jwt):
        issuer = "urn:foo"
        payload = {"some": "payload", "iss": "urn:foo"}
        token = jwt.encode(payload, "secret")
>       jwt.decode(token, "secret", issuer=issuer, algorithms=["HS256"])

tests/test_api_jwt.py:489: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImlzcyI6InVybjpmb28ifQ.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImlzcyI6InVybjpmb28ifQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_raise_exception_invalid_issuer

test_api_jwt.py::TestJWT::test_raise_exception_invalid_issuer
self = 
jwt = 

    def test_raise_exception_invalid_issuer(self, jwt):
        issuer = "urn:wrong"

        payload = {"some": "payload", "iss": "urn:foo"}

        token = jwt.encode(payload, "secret")

        with pytest.raises(InvalidIssuerError):
>           jwt.decode(token, "secret", issuer=issuer, algorithms=["HS256"])

tests/test_api_jwt.py:499: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImlzcyI6InVybjpmb28ifQ.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImlzcyI6InVybjpmb28ifQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_skip_check_audience

test_api_jwt.py::TestJWT::test_skip_check_audience
self = 
jwt = 

    def test_skip_check_audience(self, jwt):
        payload = {"some": "payload", "aud": "urn:me"}
        token = jwt.encode(payload, "secret")
>       jwt.decode(
            token,
            "secret",
            options={"verify_aud": False},
            algorithms=["HS256"],
        )

tests/test_api_jwt.py:504: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6InVybjptZSJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': False, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': False, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImF1ZCI6InVybjptZSJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_skip_check_exp

test_api_jwt.py::TestJWT::test_skip_check_exp
self = 
jwt = 

    def test_skip_check_exp(self, jwt):
        payload = {
            "some": "payload",
            "exp": datetime.now(tz=timezone.utc) - timedelta(days=1),
        }
        token = jwt.encode(payload, "secret")
>       jwt.decode(
            token,
            "secret",
            options={"verify_exp": False},
            algorithms=["HS256"],
        )

tests/test_api_jwt.py:517: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImV4cCI6MTczMjc2ODMwM30.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': False, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': False, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImV4cCI6MTczMjc2ODMwM30'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_should_raise_error_if_exp_required_but_not_present

test_api_jwt.py::TestJWT::test_decode_should_raise_error_if_exp_required_but_not_present
self = 
jwt = 

    def test_decode_should_raise_error_if_exp_required_but_not_present(self, jwt):
        payload = {
            "some": "payload",
            # exp not present
        }
        token = jwt.encode(payload, "secret")

        with pytest.raises(MissingRequiredClaimError) as exc:
>           jwt.decode(
                token,
                "secret",
                options={"require": ["exp"]},
                algorithms=["HS256"],
            )

tests/test_api_jwt.py:532: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': ['exp'], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': ['exp'], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_should_raise_error_if_iat_required_but_not_present

test_api_jwt.py::TestJWT::test_decode_should_raise_error_if_iat_required_but_not_present
self = 
jwt = 

    def test_decode_should_raise_error_if_iat_required_but_not_present(self, jwt):
        payload = {
            "some": "payload",
            # iat not present
        }
        token = jwt.encode(payload, "secret")

        with pytest.raises(MissingRequiredClaimError) as exc:
>           jwt.decode(
                token,
                "secret",
                options={"require": ["iat"]},
                algorithms=["HS256"],
            )

tests/test_api_jwt.py:549: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': ['iat'], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': ['iat'], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_should_raise_error_if_nbf_required_but_not_present

test_api_jwt.py::TestJWT::test_decode_should_raise_error_if_nbf_required_but_not_present
self = 
jwt = 

    def test_decode_should_raise_error_if_nbf_required_but_not_present(self, jwt):
        payload = {
            "some": "payload",
            # nbf not present
        }
        token = jwt.encode(payload, "secret")

        with pytest.raises(MissingRequiredClaimError) as exc:
>           jwt.decode(
                token,
                "secret",
                options={"require": ["nbf"]},
                algorithms=["HS256"],
            )

tests/test_api_jwt.py:566: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': ['nbf'], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': ['nbf'], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_skip_check_iat

test_api_jwt.py::TestJWT::test_skip_check_iat
self = 
jwt = 

    def test_skip_check_iat(self, jwt):
        payload = {
            "some": "payload",
            "iat": datetime.now(tz=timezone.utc) + timedelta(days=1),
        }
        token = jwt.encode(payload, "secret")
>       jwt.decode(
            token,
            "secret",
            options={"verify_iat": False},
            algorithms=["HS256"],
        )

tests/test_api_jwt.py:594: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImlhdCI6MTczMjk0MTEwM30.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': False, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': False, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsImlhdCI6MTczMjk0MTEwM30'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_skip_check_nbf

test_api_jwt.py::TestJWT::test_skip_check_nbf
self = 
jwt = 

    def test_skip_check_nbf(self, jwt):
        payload = {
            "some": "payload",
            "nbf": datetime.now(tz=timezone.utc) + timedelta(days=1),
        }
        token = jwt.encode(payload, "secret")
>       jwt.decode(
            token,
            "secret",
            options={"verify_nbf": False},
            algorithms=["HS256"],
        )

tests/test_api_jwt.py:607: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsIm5iZiI6MTczMjk0MTEwM30.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lIjoicGF5bG9hZCIsIm5iZiI6MTczMjk0MTEwM30'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_custom_json_encoder

test_api_jwt.py::TestJWT::test_custom_json_encoder
self = 
jwt = 

    def test_custom_json_encoder(self, jwt):
        class CustomJSONEncoder(json.JSONEncoder):
            def default(self, o):
                if isinstance(o, Decimal):
                    return "it worked"
                return super().default(o)

        data = {"some_decimal": Decimal("2.2")}

        with pytest.raises(TypeError):
            jwt.encode(data, "secret", algorithms=["HS256"])

        token = jwt.encode(data, "secret", json_encoder=CustomJSONEncoder)
>       payload = jwt.decode(token, "secret", algorithms=["HS256"])

tests/test_api_jwt.py:627: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lX2RlY2ltYWwiOiJpdCB3b3JrZWQifQ.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzb21lX2RlY2ltYWwiOiJpdCB3b3JrZWQifQ'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_with_verify_exp_option

test_api_jwt.py::TestJWT::test_decode_with_verify_exp_option
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854702, 'iss': 'jeff'}

    def test_decode_with_verify_exp_option(self, jwt, payload):
        payload["exp"] = utc_timestamp() - 1
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

>       jwt.decode(
            jwt_message,
            secret,
            algorithms=["HS256"],
            options={"verify_exp": False},
        )

tests/test_api_jwt.py:636: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzAyLCJjbGFpbSI6Imluc2FuaXR5In0.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': False, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': False, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzAyLCJjbGFpbSI6Imluc2FuaXR5In0'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_with_verify_exp_option_and_signature_off

test_api_jwt.py::TestJWT::test_decode_with_verify_exp_option_and_signature_off
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854702, 'iss': 'jeff'}

    def test_decode_with_verify_exp_option_and_signature_off(self, jwt, payload):
        payload["exp"] = utc_timestamp() - 1
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

>       jwt.decode(
            jwt_message,
            options={"verify_signature": False},
        )

tests/test_api_jwt.py:656: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzAyLCJjbGFpbSI6Imluc2FuaXR5In0.'
key = None, algorithms = None, options = {'verify_signature': False}
kwargs = {}
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
decoded = {'header': {'alg': 'none', 'typ': 'JWT'}, 'payload': b'{"iss":"jeff","exp":1732854702,"claim":"insanity"}', 'signature': b''}
payload = {'claim': 'insanity', 'exp': 1732854702, 'iss': 'jeff'}
now = datetime.datetime(2024, 11, 29, 4, 31, 43, 847841, tzinfo=datetime.timezone.utc)
exp = datetime.datetime(2024, 11, 29, 4, 31, 42, tzinfo=datetime.timezone.utc)
leeway = datetime.timedelta(0)

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, **kwargs: Any) -> dict[str, Any]:
        """
        Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        merged_options = {**self.options, **(options or {})}
        decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
        payload = self._decode_payload(decoded)

        if merged_options['verify_exp'] and 'exp' in payload:
            now = kwargs.get('now', datetime.now(timezone.utc))
            exp = datetime.fromtimestamp(payload['exp'], tz=timezone.utc)
            leeway = timedelta(seconds=kwargs.get('leeway', 0))
            if now > exp + leeway:
>               raise ExpiredSignatureError('Signature has expired')
E               jwt.exceptions.ExpiredSignatureError: Signature has expired

jwt/api_jwt.py:119: ExpiredSignatureError

test_api_jwt.py::TestJWT::test_decode_with_optional_algorithms

test_api_jwt.py::TestJWT::test_decode_with_optional_algorithms
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854718, 'iss': 'jeff'}

    def test_decode_with_optional_algorithms(self, jwt, payload):
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

        with pytest.raises(DecodeError) as exc:
            jwt.decode(jwt_message, secret)

>       assert (
            'It is required that you pass in a value for the "algorithms" argument when calling decode().'
            in str(exc.value)
        )
E       assert 'It is required that you pass in a value for the "algorithms" argument when calling decode().' in 'Algorithm "none" not allowed'
E        +  where 'Algorithm "none" not allowed' = str(DecodeError('Algorithm "none" not allowed'))
E        +    where DecodeError('Algorithm "none" not allowed') = .value

tests/test_api_jwt.py:674: AssertionError

test_api_jwt.py::TestJWT::test_decode_no_algorithms_verify_signature_false

test_api_jwt.py::TestJWT::test_decode_no_algorithms_verify_signature_false
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854718, 'iss': 'jeff'}

    def test_decode_no_algorithms_verify_signature_false(self, jwt, payload):
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

>       jwt.decode(jwt_message, secret, options={"verify_signature": False})

tests/test_api_jwt.py:683: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5In0.'
key = 'secret'
algorithms = ['ES384', 'RS384', 'EdDSA', 'HS256', 'RS256', 'RS512', ...]
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5In0'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
            raise InvalidAlgorithmError('The specified alg value is not allowed')

        if alg == 'none':
            if merged_options['verify_signature']:
                raise DecodeError('Algorithm "none" not allowed')
            if key not in [None, '', 'none']:
>               raise InvalidKeyError('When alg = "none", key must be empty or "none"')
E               NameError: name 'InvalidKeyError' is not defined

jwt/api_jws.py:284: NameError

test_api_jwt.py::TestJWT::test_decode_legacy_verify_warning

test_api_jwt.py::TestJWT::test_decode_legacy_verify_warning
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854718, 'iss': 'jeff'}

    def test_decode_legacy_verify_warning(self, jwt, payload):
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

        with pytest.deprecated_call():
            # The implicit default for options.verify_signature is True,
            # but the user sets verify to False.
>           jwt.decode(jwt_message, secret, verify=False, algorithms=["HS256"])

tests/test_api_jwt.py:692: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5In0.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5In0'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

During handling of the above exception, another exception occurred:

self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854718, 'iss': 'jeff'}

    def test_decode_legacy_verify_warning(self, jwt, payload):
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

>       with pytest.deprecated_call():
E       Failed: DID NOT WARN. No warnings of type (, , ) were emitted.
E        Emitted warnings: [].

tests/test_api_jwt.py:689: Failed

test_api_jwt.py::TestJWT::test_decode_no_options_mutation

test_api_jwt.py::TestJWT::test_decode_no_options_mutation
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854718, 'iss': 'jeff'}

    def test_decode_no_options_mutation(self, jwt, payload):
        options = {"verify_signature": True}
        orig_options = options.copy()
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)
>       jwt.decode(jwt_message, secret, options=options, algorithms=["HS256"])

tests/test_api_jwt.py:706: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5In0.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5In0'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_warns_on_unsupported_kwarg

test_api_jwt.py::TestJWT::test_decode_warns_on_unsupported_kwarg
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854718, 'iss': 'jeff'}

    def test_decode_warns_on_unsupported_kwarg(self, jwt, payload):
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

        with pytest.warns(RemovedInPyjwt3Warning) as record:
>           jwt.decode(jwt_message, secret, algorithms=["HS256"], foo="bar")

tests/test_api_jwt.py:714: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5In0.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE4LCJjbGFpbSI6Imluc2FuaXR5In0'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

During handling of the above exception, another exception occurred:

self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854718, 'iss': 'jeff'}

    def test_decode_warns_on_unsupported_kwarg(self, jwt, payload):
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

>       with pytest.warns(RemovedInPyjwt3Warning) as record:
E       Failed: DID NOT WARN. No warnings of type (,) were emitted.
E        Emitted warnings: [].

tests/test_api_jwt.py:713: Failed

test_api_jwt.py::TestJWT::test_decode_complete_warns_on_unsupported_kwarg

test_api_jwt.py::TestJWT::test_decode_complete_warns_on_unsupported_kwarg
self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854719, 'iss': 'jeff'}

    def test_decode_complete_warns_on_unsupported_kwarg(self, jwt, payload):
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

        with pytest.warns(RemovedInPyjwt3Warning) as record:
>           jwt.decode_complete(jwt_message, secret, algorithms=["HS256"], foo="bar")

tests/test_api_jwt.py:723: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE5LCJjbGFpbSI6Imluc2FuaXR5In0.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE5LCJjbGFpbSI6Imluc2FuaXR5In0'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

During handling of the above exception, another exception occurred:

self = 
jwt = 
payload = {'claim': 'insanity', 'exp': 1732854719, 'iss': 'jeff'}

    def test_decode_complete_warns_on_unsupported_kwarg(self, jwt, payload):
        secret = "secret"
        jwt_message = jwt.encode(payload, secret)

>       with pytest.warns(RemovedInPyjwt3Warning) as record:
E       Failed: DID NOT WARN. No warnings of type (,) were emitted.
E        Emitted warnings: [].

tests/test_api_jwt.py:722: Failed

test_api_jwt.py::TestJWT::test_decode_strict_aud_forbids_list_audience

test_api_jwt.py::TestJWT::test_decode_strict_aud_forbids_list_audience
self = 
jwt = 
payload = {'aud': 'urn:foo', 'claim': 'insanity', 'exp': 1732854719, 'iss': 'jeff'}

    def test_decode_strict_aud_forbids_list_audience(self, jwt, payload):
        secret = "secret"
        payload["aud"] = "urn:foo"
        jwt_message = jwt.encode(payload, secret)

        # Decodes without `strict_aud`.
>       jwt.decode(
            jwt_message,
            secret,
            audience=["urn:foo", "urn:bar"],
            options={"strict_aud": False},
            algorithms=["HS256"],
        )

tests/test_api_jwt.py:733: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE5LCJjbGFpbSI6Imluc2FuaXR5IiwiYXVkIjoidXJuOmZvbyJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'strict_aud': False, 'verify_aud': True, 'verify_exp': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'strict_aud': False, 'verify_aud': True, 'verify_exp': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE5LCJjbGFpbSI6Imluc2FuaXR5IiwiYXVkIjoidXJuOmZvbyJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_strict_aud_forbids_list_claim

test_api_jwt.py::TestJWT::test_decode_strict_aud_forbids_list_claim
self = 
jwt = 
payload = {'aud': ['urn:foo', 'urn:bar'], 'claim': 'insanity', 'exp': 1732854719, 'iss': 'jeff'}

    def test_decode_strict_aud_forbids_list_claim(self, jwt, payload):
        secret = "secret"
        payload["aud"] = ["urn:foo", "urn:bar"]
        jwt_message = jwt.encode(payload, secret)

        # Decodes without `strict_aud`.
>       jwt.decode(
            jwt_message,
            secret,
            audience="urn:foo",
            options={"strict_aud": False},
            algorithms=["HS256"],
        )

tests/test_api_jwt.py:757: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE5LCJjbGFpbSI6Imluc2FuaXR5IiwiYXVkIjpbInVybjpmb28iLCJ1cm46YmFyIl19.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'strict_aud': False, 'verify_aud': True, 'verify_exp': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'strict_aud': False, 'verify_aud': True, 'verify_exp': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE5LCJjbGFpbSI6Imluc2FuaXR5IiwiYXVkIjpbInVybjpmb28iLCJ1cm46YmFyIl19'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_strict_aud_does_not_match

test_api_jwt.py::TestJWT::test_decode_strict_aud_does_not_match
self = 
jwt = 
payload = {'aud': 'urn:foo', 'claim': 'insanity', 'exp': 1732854719, 'iss': 'jeff'}

    def test_decode_strict_aud_does_not_match(self, jwt, payload):
        secret = "secret"
        payload["aud"] = "urn:foo"
        jwt_message = jwt.encode(payload, secret)

        with pytest.raises(
            InvalidAudienceError, match=r"Audience doesn't match \(strict\)"
        ):
>           jwt.decode(
                jwt_message,
                secret,
                audience="urn:bar",
                options={"strict_aud": True},
                algorithms=["HS256"],
            )

tests/test_api_jwt.py:785: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE5LCJjbGFpbSI6Imluc2FuaXR5IiwiYXVkIjoidXJuOmZvbyJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'strict_aud': True, 'verify_aud': True, 'verify_exp': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'strict_aud': True, 'verify_aud': True, 'verify_exp': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE5LCJjbGFpbSI6Imluc2FuaXR5IiwiYXVkIjoidXJuOmZvbyJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_api_jwt.py::TestJWT::test_decode_strict_ok

test_api_jwt.py::TestJWT::test_decode_strict_ok
self = 
jwt = 
payload = {'aud': 'urn:foo', 'claim': 'insanity', 'exp': 1732854719, 'iss': 'jeff'}

    def test_decode_strict_ok(self, jwt, payload):
        secret = "secret"
        payload["aud"] = "urn:foo"
        jwt_message = jwt.encode(payload, secret)

>       jwt.decode(
            jwt_message,
            secret,
            audience="urn:foo",
            options={"strict_aud": True},
            algorithms=["HS256"],
        )

tests/test_api_jwt.py:798: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/api_jwt.py:170: in decode
    decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
jwt/api_jwt.py:111: in decode_complete
    decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE5LCJjbGFpbSI6Imluc2FuaXR5IiwiYXVkIjoidXJuOmZvbyJ9.'
key = 'secret', algorithms = ['HS256']
options = {'require': [], 'strict_aud': True, 'verify_aud': True, 'verify_exp': True, ...}
detached_payload = None, kwargs = {}
deprecated_kwargs = {'verify': 'verify_signature', 'verify_aud': 'verify_aud', 'verify_exp': 'verify_exp', 'verify_iat': 'verify_iat', ...}
old_name = 'verify_iss', new_name = 'verify_iss'
merged_options = {'require': [], 'strict_aud': True, 'verify_aud': True, 'verify_exp': True, ...}
signing_input = b'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJqZWZmIiwiZXhwIjoxNzMyODU0NzE5LCJjbGFpbSI6Imluc2FuaXR5IiwiYXVkIjoidXJuOmZvbyJ9'
crypto_segment = b''

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
        """Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            detached_payload: The detached payload to use for verification.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        deprecated_kwargs = {
            'verify': 'verify_signature',
            'verify_exp': 'verify_exp',
            'verify_iat': 'verify_iat',
            'verify_nbf': 'verify_nbf',
            'verify_aud': 'verify_aud',
            'verify_iss': 'verify_iss',
        }

        options = options or {}
        for old_name, new_name in deprecated_kwargs.items():
            if old_name in kwargs:
                warnings.warn(
                    f'The {old_name} parameter is deprecated. '
                    f'Please use {new_name} in options instead.',
                    category=DeprecationWarning,
                    stacklevel=2
                )
                options[new_name] = kwargs.pop(old_name)

        for kwarg in kwargs:
            warnings.warn(
                f'The "{kwarg}" argument is not supported and will be ignored.',
                category=RemovedInPyjwt3Warning,
                stacklevel=2
            )

        merged_options = {**self.options}
        if options:
            if not isinstance(options, dict):
                raise TypeError('options must be a dict')
            merged_options.update(options)

        if isinstance(jwt, str):
            jwt = jwt.encode('utf-8')

        if not isinstance(jwt, bytes):
            raise DecodeError('Invalid token type')

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise InvalidTokenError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, dict):
            raise DecodeError('Invalid header string: must be a json object')

        if header.get('b64', True):
            try:
                payload = base64url_decode(payload_segment)
            except (TypeError, binascii.Error):
                raise DecodeError('Invalid payload padding')
        else:
            if detached_payload is None:
                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
            payload = detached_payload

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        if algorithms is None:
            algorithms = list(self._valid_algs)

        if not algorithms and merged_options['verify_signature']:
            raise DecodeError('No algorithms were specified')

        try:
            alg = header['alg']
        except KeyError:
            raise InvalidTokenError('Missing algorithm ("alg") in headers')

        if alg not in algorithms:
>           raise InvalidAlgorithmError('The specified alg value is not allowed')
E           jwt.exceptions.InvalidAlgorithmError: The specified alg value is not allowed

jwt/api_jws.py:278: InvalidAlgorithmError

test_jwks_client.py::TestPyJWKClient::test_get_signing_keys_raises_if_none_found

test_jwks_client.py::TestPyJWKClient::test_get_signing_keys_raises_if_none_found
self = 

    def test_get_signing_keys_raises_if_none_found(self):
        url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"

        mocked_key = RESPONSE_DATA_WITH_MATCHING_KID["keys"][0].copy()
        mocked_key["use"] = "enc"
        response = {"keys": [mocked_key]}
        with mocked_success_response(response):
            jwks_client = PyJWKClient(url)

            with pytest.raises(PyJWKClientError) as exc:
                jwks_client.get_signing_keys()

>       assert "The JWKS endpoint did not contain any signing keys" in str(exc.value)
E       AssertionError: assert 'The JWKS endpoint did not contain any signing keys' in 'No signing keys found in JWKS'
E        +  where 'No signing keys found in JWKS' = str(PyJWKClientError('No signing keys found in JWKS'))
E        +    where PyJWKClientError('No signing keys found in JWKS') = .value

tests/test_jwks_client.py:149: AssertionError

test_jwks_client.py::TestPyJWKClient::test_get_signing_key_from_jwt

test_jwks_client.py::TestPyJWKClient::test_get_signing_key_from_jwt
self = 
token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRd...kc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA'
refresh_jwks = True

    def get_signing_key_from_jwt(self, token: str, refresh_jwks: bool=True) -> PyJWK:
        """Return the signing key from the JWKS that matches the kid in the token header.

        Args:
            token: The JWT token to get the key for.
            refresh_jwks: Whether to refresh the JWKS if the key is not found.
        """
        try:
>           headers = decode_token(token, options={'verify_signature': False})['header']

jwt/jwks_client.py:109: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRd...kc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA'
key = None, algorithms = None, options = {'verify_signature': False}
kwargs = {}
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
decoded = {'header': {'alg': 'RS256', 'kid': 'NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw', 'typ': 'JWT'}, 'payload':...f;\x1b\xdf]5K\\\xbd\xa3zNfx~\xa7\xf9U\xa0\xac\x17h0Q\xfd\xdd\x01,\xcc@\xe8\xc00\x04\x85\x03\xc7V\xcd1\x8f\xb4\x88\xcc'}
payload = {'aud': 'https://expenses-api', 'azp': 'aW4Cca79xReLWUz0aE2H6kD0O3cXBVtC', 'exp': 1572006964, 'gty': 'client-credentials', ...}
now = datetime.datetime(2024, 11, 29, 4, 31, 44, 182936, tzinfo=datetime.timezone.utc)
exp = datetime.datetime(2019, 10, 25, 12, 36, 4, tzinfo=datetime.timezone.utc)
leeway = datetime.timedelta(0)

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, **kwargs: Any) -> dict[str, Any]:
        """
        Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        merged_options = {**self.options, **(options or {})}
        decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
        payload = self._decode_payload(decoded)

        if merged_options['verify_exp'] and 'exp' in payload:
            now = kwargs.get('now', datetime.now(timezone.utc))
            exp = datetime.fromtimestamp(payload['exp'], tz=timezone.utc)
            leeway = timedelta(seconds=kwargs.get('leeway', 0))
            if now > exp + leeway:
>               raise ExpiredSignatureError('Signature has expired')
E               jwt.exceptions.ExpiredSignatureError: Signature has expired

jwt/api_jwt.py:119: ExpiredSignatureError

During handling of the above exception, another exception occurred:

self = 

    def test_get_signing_key_from_jwt(self):
        token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRdyJ9.eyJpc3MiOiJodHRwczovL2Rldi04N2V2eDlydS5hdXRoMC5jb20vIiwic3ViIjoiYVc0Q2NhNzl4UmVMV1V6MGFFMkg2a0QwTzNjWEJWdENAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vZXhwZW5zZXMtYXBpIiwiaWF0IjoxNTcyMDA2OTU0LCJleHAiOjE1NzIwMDY5NjQsImF6cCI6ImFXNENjYTc5eFJlTFdVejBhRTJINmtEME8zY1hCVnRDIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.PUxE7xn52aTCohGiWoSdMBZGiYAHwE5FYie0Y1qUT68IHSTXwXVd6hn02HTah6epvHHVKA2FqcFZ4GGv5VTHEvYpeggiiZMgbxFrmTEY0csL6VNkX1eaJGcuehwQCRBKRLL3zKmA5IKGy5GeUnIbpPHLHDxr-GXvgFzsdsyWlVQvPX2xjeaQ217r2PtxDeqjlf66UYl6oY6AqNS8DH3iryCvIfCcybRZkc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA"
        url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"

        with mocked_success_response(RESPONSE_DATA_WITH_MATCHING_KID):
            jwks_client = PyJWKClient(url)
>           signing_key = jwks_client.get_signing_key_from_jwt(token)

tests/test_jwks_client.py:202: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRd...kc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA'
refresh_jwks = True

    def get_signing_key_from_jwt(self, token: str, refresh_jwks: bool=True) -> PyJWK:
        """Return the signing key from the JWKS that matches the kid in the token header.

        Args:
            token: The JWT token to get the key for.
            refresh_jwks: Whether to refresh the JWKS if the key is not found.
        """
        try:
            headers = decode_token(token, options={'verify_signature': False})['header']
        except Exception as e:
>           raise PyJWKClientError(f'Failed to decode JWT headers: {str(e)}')
E           jwt.exceptions.PyJWKClientError: Failed to decode JWT headers: Signature has expired

jwt/jwks_client.py:111: PyJWKClientError

test_jwks_client.py::TestPyJWKClient::test_failed_request_should_raise_connection_error

test_jwks_client.py::TestPyJWKClient::test_failed_request_should_raise_connection_error
self = 
token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRd...kc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA'
refresh_jwks = True

    def get_signing_key_from_jwt(self, token: str, refresh_jwks: bool=True) -> PyJWK:
        """Return the signing key from the JWKS that matches the kid in the token header.

        Args:
            token: The JWT token to get the key for.
            refresh_jwks: Whether to refresh the JWKS if the key is not found.
        """
        try:
>           headers = decode_token(token, options={'verify_signature': False})['header']

jwt/jwks_client.py:109: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRd...kc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA'
key = None, algorithms = None, options = {'verify_signature': False}
kwargs = {}
merged_options = {'require': [], 'verify_aud': True, 'verify_exp': True, 'verify_iat': True, ...}
decoded = {'header': {'alg': 'RS256', 'kid': 'NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw', 'typ': 'JWT'}, 'payload':...f;\x1b\xdf]5K\\\xbd\xa3zNfx~\xa7\xf9U\xa0\xac\x17h0Q\xfd\xdd\x01,\xcc@\xe8\xc00\x04\x85\x03\xc7V\xcd1\x8f\xb4\x88\xcc'}
payload = {'aud': 'https://expenses-api', 'azp': 'aW4Cca79xReLWUz0aE2H6kD0O3cXBVtC', 'exp': 1572006964, 'gty': 'client-credentials', ...}
now = datetime.datetime(2024, 11, 29, 4, 31, 48, 215542, tzinfo=datetime.timezone.utc)
exp = datetime.datetime(2019, 10, 25, 12, 36, 4, tzinfo=datetime.timezone.utc)
leeway = datetime.timedelta(0)

    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, **kwargs: Any) -> dict[str, Any]:
        """
        Decodes a JWT and returns a dict of the token contents.

        Args:
            jwt: The JWT to decode.
            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
            options: A dict of options for decoding. If None, use default options.
            **kwargs: Additional options for decoding.

        Returns:
            A dict including:
                - header: A dict of the JWT header
                - payload: The decoded payload
                - signature: The signature of the JWT
        """
        merged_options = {**self.options, **(options or {})}
        decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
        payload = self._decode_payload(decoded)

        if merged_options['verify_exp'] and 'exp' in payload:
            now = kwargs.get('now', datetime.now(timezone.utc))
            exp = datetime.fromtimestamp(payload['exp'], tz=timezone.utc)
            leeway = timedelta(seconds=kwargs.get('leeway', 0))
            if now > exp + leeway:
>               raise ExpiredSignatureError('Signature has expired')
E               jwt.exceptions.ExpiredSignatureError: Signature has expired

jwt/api_jwt.py:119: ExpiredSignatureError

During handling of the above exception, another exception occurred:

self = 

    def test_failed_request_should_raise_connection_error(self):
        token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRdyJ9.eyJpc3MiOiJodHRwczovL2Rldi04N2V2eDlydS5hdXRoMC5jb20vIiwic3ViIjoiYVc0Q2NhNzl4UmVMV1V6MGFFMkg2a0QwTzNjWEJWdENAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vZXhwZW5zZXMtYXBpIiwiaWF0IjoxNTcyMDA2OTU0LCJleHAiOjE1NzIwMDY5NjQsImF6cCI6ImFXNENjYTc5eFJlTFdVejBhRTJINmtEME8zY1hCVnRDIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.PUxE7xn52aTCohGiWoSdMBZGiYAHwE5FYie0Y1qUT68IHSTXwXVd6hn02HTah6epvHHVKA2FqcFZ4GGv5VTHEvYpeggiiZMgbxFrmTEY0csL6VNkX1eaJGcuehwQCRBKRLL3zKmA5IKGy5GeUnIbpPHLHDxr-GXvgFzsdsyWlVQvPX2xjeaQ217r2PtxDeqjlf66UYl6oY6AqNS8DH3iryCvIfCcybRZkc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA"
        url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"

        jwks_client = PyJWKClient(url)
        with pytest.raises(PyJWKClientConnectionError):
            with mocked_failed_response():
>               jwks_client.get_signing_key_from_jwt(token)

tests/test_jwks_client.py:294: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRd...kc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA'
refresh_jwks = True

    def get_signing_key_from_jwt(self, token: str, refresh_jwks: bool=True) -> PyJWK:
        """Return the signing key from the JWKS that matches the kid in the token header.

        Args:
            token: The JWT token to get the key for.
            refresh_jwks: Whether to refresh the JWKS if the key is not found.
        """
        try:
            headers = decode_token(token, options={'verify_signature': False})['header']
        except Exception as e:
>           raise PyJWKClientError(f'Failed to decode JWT headers: {str(e)}')
E           jwt.exceptions.PyJWKClientError: Failed to decode JWT headers: Signature has expired

jwt/jwks_client.py:111: PyJWKClientError

test_jwks_client.py::TestPyJWKClient::test_get_jwt_set_refresh_cache

test_jwks_client.py::TestPyJWKClient::test_get_jwt_set_refresh_cache
self = 

    def test_get_jwt_set_refresh_cache(self):
        url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"
        jwks_client = PyJWKClient(url)

        kid = "NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw"

        # The first call will return response with no matching kid,
        # the function should make another call to try to refresh the cache.
        with mocked_first_call_wrong_kid_second_call_correct_kid(
            RESPONSE_DATA_NO_MATCHING_KID, RESPONSE_DATA_WITH_MATCHING_KID
        ) as call_data:
>           jwks_client.get_signing_key(kid)

tests/test_jwks_client.py:307: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
kid = 'NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw'

    def get_signing_key(self, kid: str) -> PyJWK:
        """Return the signing key from the JWKS that matches the provided kid.

        Args:
            kid: The key ID to search for.
        """
        signing_keys = self.get_signing_keys()
        for key in signing_keys:
            if key.key_id == kid:
                return key

        # If no key is found, try refreshing the JWKS once
        signing_keys = self.get_signing_keys()
        for key in signing_keys:
            if key.key_id == kid:
                return key

>       raise PyJWKClientError(f'Unable to find a signing key that matches: {kid}')
E       jwt.exceptions.PyJWKClientError: Unable to find a signing key that matches: NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw

jwt/jwks_client.py:99: PyJWKClientError

test_jwks_client.py::TestPyJWKClient::test_get_jwt_set_timeout

test_jwks_client.py::TestPyJWKClient::test_get_jwt_set_timeout
self = 

    def test_get_jwt_set_timeout(self):
        url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"
        jwks_client = PyJWKClient(url, timeout=5)

        with pytest.raises(PyJWKClientError) as exc:
            with mocked_timeout():
>               jwks_client.get_jwk_set()

tests/test_jwks_client.py:336: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
jwt/jwks_client.py:55: in get_jwk_set
    data = self.fetch_data()
jwt/jwks_client.py:38: in fetch_data
    response = urllib.request.urlopen(request, timeout=self.timeout)
/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/unittest/mock.py:1137: in __call__
    return self._mock_call(*args, **kwargs)
/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/unittest/mock.py:1141: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
args = (,)
kwargs = {'timeout': 5}, effect = TimeoutError('timed out')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method

        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               TimeoutError: timed out

/root/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/unittest/mock.py:1196: TimeoutError

test_utils.py::test_to_base64url_uint[-1-]

test_utils.py::test_to_base64url_uint[-1-]
inputval = -1, expected = ''

    @pytest.mark.parametrize(
        "inputval,expected",
        [
            (0, b"AA"),
            (1, b"AQ"),
            (255, b"_w"),
            (65537, b"AQAB"),
            (123456789, b"B1vNFQ"),
            pytest.param(-1, "", marks=pytest.mark.xfail(raises=ValueError)),
        ],
    )
    def test_to_base64url_uint(inputval, expected):
>       actual = to_base64url_uint(inputval)

tests/test_utils.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

val = -1

    def to_base64url_uint(val: int) -> bytes:
        if val < 0:
>           raise ValueError('Must be a positive integer')
E           ValueError: Must be a positive integer

jwt/utils.py:34: ValueError

Patch diff

diff --git a/jwt/algorithms.py b/jwt/algorithms.py
index b3897bf..0a18f4e 100644
--- a/jwt/algorithms.py
+++ b/jwt/algorithms.py
@@ -38,7 +38,28 @@ def get_default_algorithms() -> dict[str, Algorithm]:
     """
     Returns the algorithms that are implemented by the library.
     """
-    pass
+    default_algorithms = {
+        'none': NoneAlgorithm(),
+        'HS256': HMACAlgorithm(HMACAlgorithm.SHA256),
+        'HS384': HMACAlgorithm(HMACAlgorithm.SHA384),
+        'HS512': HMACAlgorithm(HMACAlgorithm.SHA512),
+    }
+
+    if has_crypto:
+        default_algorithms.update({
+            'RS256': RSAAlgorithm(RSAAlgorithm.SHA256),
+            'RS384': RSAAlgorithm(RSAAlgorithm.SHA384),
+            'RS512': RSAAlgorithm(RSAAlgorithm.SHA512),
+            'ES256': ECAlgorithm(ECAlgorithm.SHA256),
+            'ES384': ECAlgorithm(ECAlgorithm.SHA384),
+            'ES512': ECAlgorithm(ECAlgorithm.SHA512),
+            'PS256': RSAPSSAlgorithm(RSAPSSAlgorithm.SHA256),
+            'PS384': RSAPSSAlgorithm(RSAPSSAlgorithm.SHA384),
+            'PS512': RSAPSSAlgorithm(RSAPSSAlgorithm.SHA512),
+            'EdDSA': OKPAlgorithm(),
+        })
+
+    return default_algorithms

 class Algorithm(ABC):
     """
@@ -51,7 +72,17 @@ class Algorithm(ABC):

         If there is no hash algorithm, raises a NotImplementedError.
         """
-        pass
+        if not hasattr(self, 'hash_alg'):
+            raise NotImplementedError('Algorithm does not have a hash algorithm')
+        
+        if has_crypto and isinstance(self.hash_alg, type) and issubclass(self.hash_alg, hashes.HashAlgorithm):
+            h = hashes.Hash(self.hash_alg(), backend=default_backend())
+            h.update(bytestr)
+            return h.finalize()
+        else:
+            h = self.hash_alg()
+            h.update(bytestr)
+            return h.digest()

     @abstractmethod
     def prepare_key(self, key: Any) -> Any:
@@ -98,6 +129,24 @@ class NoneAlgorithm(Algorithm):
     Placeholder for use when no signing or verification
     operations are required.
     """
+    def prepare_key(self, key: Any) -> None:
+        if key not in [None, '', 'none']:
+            raise InvalidKeyError('When alg = "none", key must be empty or "none"')
+        return None
+
+    def sign(self, msg: bytes, key: Any) -> bytes:
+        return b''
+
+    def verify(self, msg: bytes, key: Any, sig: bytes) -> bool:
+        return sig == b''
+
+    @staticmethod
+    def to_jwk(key_obj: Any, as_dict: bool = False) -> NoReturn:
+        raise NotImplementedError('Algorithm "none" can\'t be exported as JWK')
+
+    @staticmethod
+    def from_jwk(jwk: str | JWKDict) -> NoReturn:
+        raise NotImplementedError('Algorithm "none" can\'t be imported from JWK')

 class HMACAlgorithm(Algorithm):
     """
@@ -110,6 +159,51 @@ class HMACAlgorithm(Algorithm):

     def __init__(self, hash_alg: HashlibHash) -> None:
         self.hash_alg = hash_alg
+
+    def prepare_key(self, key: Union[str, bytes]) -> bytes:
+        if not isinstance(key, (str, bytes)):
+            raise TypeError('Expected a string value')
+        key = force_bytes(key)
+        if is_pem_format(key) or is_ssh_key(key):
+            raise InvalidKeyError('The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.')
+        return key
+
+    def sign(self, msg: bytes, key: Union[str, bytes]) -> bytes:
+        key = self.prepare_key(key)
+        h = hmac.new(key, msg, self.hash_alg)
+        return h.digest()
+
+    def verify(self, msg: bytes, key: Union[str, bytes], sig: bytes) -> bool:
+        key = self.prepare_key(key)
+        h = hmac.new(key, msg, self.hash_alg)
+        try:
+            return hmac.compare_digest(sig, h.digest())
+        except TypeError:
+            return False
+
+    @staticmethod
+    def to_jwk(key_obj: Union[str, bytes], as_dict: bool = False) -> Union[str, JWKDict]:
+        key_bytes = force_bytes(key_obj)
+        jwk = {
+            'kty': 'oct',
+            'k': base64url_encode(key_bytes).decode('ascii')
+        }
+        if as_dict:
+            return jwk
+        return json.dumps(jwk)
+
+    @staticmethod
+    def from_jwk(jwk: Union[str, JWKDict]) -> bytes:
+        if isinstance(jwk, str):
+            jwk = json.loads(jwk)
+        if not isinstance(jwk, dict):
+            raise InvalidKeyError('Key must be a dict or a string')
+        if jwk.get('kty') != 'oct':
+            raise InvalidKeyError('Not an HMAC key')
+        k = jwk.get('k')
+        if not k:
+            raise InvalidKeyError('k parameter is required')
+        return base64url_decode(k)
 if has_crypto:

     class RSAAlgorithm(Algorithm):
@@ -124,6 +218,107 @@ if has_crypto:
         def __init__(self, hash_alg: type[hashes.HashAlgorithm]) -> None:
             self.hash_alg = hash_alg

+        def prepare_key(self, key: Union[str, bytes, RSAPrivateKey, RSAPublicKey]) -> Union[RSAPrivateKey, RSAPublicKey]:
+            if isinstance(key, (RSAPrivateKey, RSAPublicKey)):
+                return key
+
+            key = force_bytes(key)
+            if is_pem_format(key):
+                try:
+                    return load_pem_private_key(key, password=None, backend=default_backend())
+                except ValueError:
+                    try:
+                        return load_pem_public_key(key, backend=default_backend())
+                    except ValueError:
+                        raise InvalidKeyError('Invalid PEM format')
+            elif is_ssh_key(key):
+                try:
+                    return load_ssh_public_key(key, backend=default_backend())
+                except ValueError:
+                    raise InvalidKeyError('Invalid SSH key format')
+            else:
+                raise InvalidKeyError('Invalid key format')
+
+        def sign(self, msg: bytes, key: Union[str, bytes, RSAPrivateKey]) -> bytes:
+            key_obj = self.prepare_key(key)
+            if not isinstance(key_obj, RSAPrivateKey):
+                raise TypeError('Key must be an RSAPrivateKey instance')
+
+            padder = padding.PKCS1v15()
+            return key_obj.sign(msg, padder, self.hash_alg())
+
+        def verify(self, msg: bytes, key: Union[str, bytes, RSAPrivateKey, RSAPublicKey], sig: bytes) -> bool:
+            key_obj = self.prepare_key(key)
+            if not isinstance(key_obj, (RSAPrivateKey, RSAPublicKey)):
+                raise TypeError('Key must be an RSA key instance')
+
+            verifier = key_obj if isinstance(key_obj, RSAPublicKey) else key_obj.public_key()
+            padder = padding.PKCS1v15()
+            try:
+                verifier.verify(sig, msg, padder, self.hash_alg())
+                return True
+            except InvalidSignature:
+                return False
+
+        @staticmethod
+        def to_jwk(key_obj: Union[RSAPrivateKey, RSAPublicKey], as_dict: bool = False) -> Union[str, JWKDict]:
+            if isinstance(key_obj, RSAPrivateKey):
+                numbers = key_obj.private_numbers()
+                jwk = {
+                    'kty': 'RSA',
+                    'n': to_base64url_uint(numbers.public_numbers.n).decode('ascii'),
+                    'e': to_base64url_uint(numbers.public_numbers.e).decode('ascii'),
+                    'd': to_base64url_uint(numbers.d).decode('ascii'),
+                    'p': to_base64url_uint(numbers.p).decode('ascii'),
+                    'q': to_base64url_uint(numbers.q).decode('ascii'),
+                    'dp': to_base64url_uint(numbers.dmp1).decode('ascii'),
+                    'dq': to_base64url_uint(numbers.dmq1).decode('ascii'),
+                    'qi': to_base64url_uint(numbers.iqmp).decode('ascii')
+                }
+            else:
+                numbers = key_obj.public_numbers()
+                jwk = {
+                    'kty': 'RSA',
+                    'n': to_base64url_uint(numbers.n).decode('ascii'),
+                    'e': to_base64url_uint(numbers.e).decode('ascii')
+                }
+
+            if as_dict:
+                return jwk
+            return json.dumps(jwk)
+
+        @staticmethod
+        def from_jwk(jwk: Union[str, JWKDict]) -> Union[RSAPrivateKey, RSAPublicKey]:
+            if isinstance(jwk, str):
+                jwk = json.loads(jwk)
+            if not isinstance(jwk, dict):
+                raise InvalidKeyError('Key must be a dict or a string')
+            if jwk.get('kty') != 'RSA':
+                raise InvalidKeyError('Not an RSA key')
+
+            if 'd' in jwk and 'p' in jwk and 'q' in jwk:
+                # Private key
+                numbers = RSAPrivateNumbers(
+                    d=from_base64url_uint(jwk['d']),
+                    p=from_base64url_uint(jwk['p']),
+                    q=from_base64url_uint(jwk['q']),
+                    dmp1=from_base64url_uint(jwk['dp']),
+                    dmq1=from_base64url_uint(jwk['dq']),
+                    iqmp=from_base64url_uint(jwk['qi']),
+                    public_numbers=RSAPublicNumbers(
+                        e=from_base64url_uint(jwk['e']),
+                        n=from_base64url_uint(jwk['n'])
+                    )
+                )
+                return numbers.private_key(backend=default_backend())
+            else:
+                # Public key
+                numbers = RSAPublicNumbers(
+                    e=from_base64url_uint(jwk['e']),
+                    n=from_base64url_uint(jwk['n'])
+                )
+                return numbers.public_key(backend=default_backend())
+
     class ECAlgorithm(Algorithm):
         """
         Performs signing and verification operations using
@@ -136,10 +331,150 @@ if has_crypto:
         def __init__(self, hash_alg: type[hashes.HashAlgorithm]) -> None:
             self.hash_alg = hash_alg

+        def prepare_key(self, key: Union[str, bytes, EllipticCurvePrivateKey, EllipticCurvePublicKey]) -> Union[EllipticCurvePrivateKey, EllipticCurvePublicKey]:
+            if isinstance(key, (EllipticCurvePrivateKey, EllipticCurvePublicKey)):
+                return key
+
+            key = force_bytes(key)
+            if is_pem_format(key):
+                try:
+                    return load_pem_private_key(key, password=None, backend=default_backend())
+                except ValueError:
+                    try:
+                        return load_pem_public_key(key, backend=default_backend())
+                    except ValueError:
+                        raise InvalidKeyError('Invalid PEM format')
+            elif is_ssh_key(key):
+                try:
+                    return load_ssh_public_key(key, backend=default_backend())
+                except ValueError:
+                    raise InvalidKeyError('Invalid SSH key format')
+            else:
+                raise InvalidKeyError('Invalid key format')
+
+        def sign(self, msg: bytes, key: Union[str, bytes, EllipticCurvePrivateKey]) -> bytes:
+            key_obj = self.prepare_key(key)
+            if not isinstance(key_obj, EllipticCurvePrivateKey):
+                raise TypeError('Key must be an EllipticCurvePrivateKey instance')
+
+            signature = key_obj.sign(msg, ECDSA(self.hash_alg()))
+            return der_to_raw_signature(signature, key_obj.curve)
+
+        def verify(self, msg: bytes, key: Union[str, bytes, EllipticCurvePrivateKey, EllipticCurvePublicKey], sig: bytes) -> bool:
+            key_obj = self.prepare_key(key)
+            if not isinstance(key_obj, (EllipticCurvePrivateKey, EllipticCurvePublicKey)):
+                raise TypeError('Key must be an EC key instance')
+
+            verifier = key_obj if isinstance(key_obj, EllipticCurvePublicKey) else key_obj.public_key()
+            curve = verifier.curve
+
+            try:
+                der_sig = raw_to_der_signature(sig, curve)
+                verifier.verify(der_sig, msg, ECDSA(self.hash_alg()))
+                return True
+            except (InvalidSignature, ValueError):
+                return False
+
+        @staticmethod
+        def to_jwk(key_obj: Union[EllipticCurvePrivateKey, EllipticCurvePublicKey], as_dict: bool = False) -> Union[str, JWKDict]:
+            if isinstance(key_obj, EllipticCurvePrivateKey):
+                numbers = key_obj.private_numbers()
+                jwk = {
+                    'kty': 'EC',
+                    'crv': {
+                        SECP256K1: 'P-256K',
+                        SECP256R1: 'P-256',
+                        SECP384R1: 'P-384',
+                        SECP521R1: 'P-521'
+                    }[type(numbers.public_numbers.curve)],
+                    'x': to_base64url_uint(numbers.public_numbers.x).decode('ascii'),
+                    'y': to_base64url_uint(numbers.public_numbers.y).decode('ascii'),
+                    'd': to_base64url_uint(numbers.private_value).decode('ascii')
+                }
+            else:
+                numbers = key_obj.public_numbers()
+                jwk = {
+                    'kty': 'EC',
+                    'crv': {
+                        SECP256K1: 'P-256K',
+                        SECP256R1: 'P-256',
+                        SECP384R1: 'P-384',
+                        SECP521R1: 'P-521'
+                    }[type(numbers.curve)],
+                    'x': to_base64url_uint(numbers.x).decode('ascii'),
+                    'y': to_base64url_uint(numbers.y).decode('ascii')
+                }
+
+            if as_dict:
+                return jwk
+            return json.dumps(jwk)
+
+        @staticmethod
+        def from_jwk(jwk: Union[str, JWKDict]) -> Union[EllipticCurvePrivateKey, EllipticCurvePublicKey]:
+            if isinstance(jwk, str):
+                jwk = json.loads(jwk)
+            if not isinstance(jwk, dict):
+                raise InvalidKeyError('Key must be a dict or a string')
+            if jwk.get('kty') != 'EC':
+                raise InvalidKeyError('Not an EC key')
+
+            curve = {
+                'P-256K': SECP256K1,
+                'P-256': SECP256R1,
+                'P-384': SECP384R1,
+                'P-521': SECP521R1
+            }[jwk['crv']]()
+
+            if 'd' in jwk:
+                # Private key
+                numbers = EllipticCurvePrivateNumbers(
+                    private_value=from_base64url_uint(jwk['d']),
+                    public_numbers=EllipticCurvePublicNumbers(
+                        x=from_base64url_uint(jwk['x']),
+                        y=from_base64url_uint(jwk['y']),
+                        curve=curve
+                    )
+                )
+                return numbers.private_key(backend=default_backend())
+            else:
+                # Public key
+                numbers = EllipticCurvePublicNumbers(
+                    x=from_base64url_uint(jwk['x']),
+                    y=from_base64url_uint(jwk['y']),
+                    curve=curve
+                )
+                return numbers.public_key(backend=default_backend())
+
     class RSAPSSAlgorithm(RSAAlgorithm):
         """
         Performs a signature using RSASSA-PSS with MGF1
         """
+        def sign(self, msg: bytes, key: Union[str, bytes, RSAPrivateKey]) -> bytes:
+            key_obj = self.prepare_key(key)
+            if not isinstance(key_obj, RSAPrivateKey):
+                raise TypeError('Key must be an RSAPrivateKey instance')
+
+            padder = padding.PSS(
+                mgf=padding.MGF1(self.hash_alg()),
+                salt_length=padding.PSS.MAX_LENGTH
+            )
+            return key_obj.sign(msg, padder, self.hash_alg())
+
+        def verify(self, msg: bytes, key: Union[str, bytes, RSAPrivateKey, RSAPublicKey], sig: bytes) -> bool:
+            key_obj = self.prepare_key(key)
+            if not isinstance(key_obj, (RSAPrivateKey, RSAPublicKey)):
+                raise TypeError('Key must be an RSA key instance')
+
+            verifier = key_obj if isinstance(key_obj, RSAPublicKey) else key_obj.public_key()
+            padder = padding.PSS(
+                mgf=padding.MGF1(self.hash_alg()),
+                salt_length=padding.PSS.MAX_LENGTH
+            )
+            try:
+                verifier.verify(sig, msg, padder, self.hash_alg())
+                return True
+            except InvalidSignature:
+                return False

     class OKPAlgorithm(Algorithm):
         """
@@ -151,6 +486,27 @@ if has_crypto:
         def __init__(self, **kwargs: Any) -> None:
             pass

+        def prepare_key(self, key: Union[str, bytes, AllowedOKPKeys]) -> AllowedOKPKeys:
+            if isinstance(key, (Ed25519PrivateKey, Ed25519PublicKey, Ed448PrivateKey, Ed448PublicKey)):
+                return key
+
+            key = force_bytes(key)
+            if is_pem_format(key):
+                try:
+                    return load_pem_private_key(key, password=None, backend=default_backend())
+                except ValueError:
+                    try:
+                        return load_pem_public_key(key, backend=default_backend())
+                    except ValueError:
+                        raise InvalidKeyError('Invalid PEM format')
+            elif is_ssh_key(key):
+                try:
+                    return load_ssh_public_key(key, backend=default_backend())
+                except ValueError:
+                    raise InvalidKeyError('Invalid SSH key format')
+            else:
+                raise InvalidKeyError('Invalid key format')
+
         def sign(self, msg: str | bytes, key: Ed25519PrivateKey | Ed448PrivateKey) -> bytes:
             """
             Sign a message ``msg`` using the EdDSA private key ``key``
@@ -159,7 +515,11 @@ if has_crypto:
                 or :class:`.Ed448PrivateKey` isinstance
             :return bytes signature: The signature, as bytes
             """
-            pass
+            msg_bytes = force_bytes(msg)
+            key_obj = self.prepare_key(key)
+            if not isinstance(key_obj, (Ed25519PrivateKey, Ed448PrivateKey)):
+                raise TypeError('Key must be an Ed25519PrivateKey or Ed448PrivateKey instance')
+            return key_obj.sign(msg_bytes)

         def verify(self, msg: str | bytes, key: AllowedOKPKeys, sig: str | bytes) -> bool:
             """
@@ -171,4 +531,85 @@ if has_crypto:
                 A private or public EdDSA key instance
             :return bool verified: True if signature is valid, False if not.
             """
-            pass
\ No newline at end of file
+            msg_bytes = force_bytes(msg)
+            sig_bytes = force_bytes(sig)
+            key_obj = self.prepare_key(key)
+
+            if isinstance(key_obj, (Ed25519PrivateKey, Ed448PrivateKey)):
+                verifier = key_obj.public_key()
+            else:
+                verifier = key_obj
+
+            try:
+                verifier.verify(sig_bytes, msg_bytes)
+                return True
+            except InvalidSignature:
+                return False
+
+        @staticmethod
+        def to_jwk(key_obj: AllowedOKPKeys, as_dict: bool = False) -> Union[str, JWKDict]:
+            if isinstance(key_obj, (Ed25519PrivateKey, Ed25519PublicKey)):
+                crv = 'Ed25519'
+            elif isinstance(key_obj, (Ed448PrivateKey, Ed448PublicKey)):
+                crv = 'Ed448'
+            else:
+                raise TypeError('Key must be an EdDSA key instance')
+
+            if isinstance(key_obj, (Ed25519PrivateKey, Ed448PrivateKey)):
+                private_bytes = key_obj.private_bytes(
+                    encoding=Encoding.Raw,
+                    format=PrivateFormat.Raw,
+                    encryption_algorithm=NoEncryption()
+                )
+                public_bytes = key_obj.public_key().public_bytes(
+                    encoding=Encoding.Raw,
+                    format=PublicFormat.Raw
+                )
+                jwk = {
+                    'kty': 'OKP',
+                    'crv': crv,
+                    'x': base64url_encode(public_bytes).decode('ascii'),
+                    'd': base64url_encode(private_bytes).decode('ascii')
+                }
+            else:
+                public_bytes = key_obj.public_bytes(
+                    encoding=Encoding.Raw,
+                    format=PublicFormat.Raw
+                )
+                jwk = {
+                    'kty': 'OKP',
+                    'crv': crv,
+                    'x': base64url_encode(public_bytes).decode('ascii')
+                }
+
+            if as_dict:
+                return jwk
+            return json.dumps(jwk)
+
+        @staticmethod
+        def from_jwk(jwk: Union[str, JWKDict]) -> AllowedOKPKeys:
+            if isinstance(jwk, str):
+                jwk = json.loads(jwk)
+            if not isinstance(jwk, dict):
+                raise InvalidKeyError('Key must be a dict or a string')
+            if jwk.get('kty') != 'OKP':
+                raise InvalidKeyError('Not an OKP key')
+
+            curve = jwk.get('crv')
+            if curve not in ['Ed25519', 'Ed448']:
+                raise InvalidKeyError('Invalid curve')
+
+            x = base64url_decode(jwk['x'])
+            if 'd' in jwk:
+                # Private key
+                d = base64url_decode(jwk['d'])
+                if curve == 'Ed25519':
+                    return Ed25519PrivateKey.from_private_bytes(d)
+                else:
+                    return Ed448PrivateKey.from_private_bytes(d)
+            else:
+                # Public key
+                if curve == 'Ed25519':
+                    return Ed25519PublicKey.from_public_bytes(x)
+                else:
+                    return Ed448PublicKey.from_public_bytes(x)
\ No newline at end of file
diff --git a/jwt/api_jwk.py b/jwt/api_jwk.py
index 1d55501..89f1902 100644
--- a/jwt/api_jwk.py
+++ b/jwt/api_jwk.py
@@ -49,6 +49,34 @@ class PyJWK:
             raise PyJWKError(f'Unable to find an algorithm for key: {self._jwk_data}')
         self.key = self.Algorithm.from_jwk(self._jwk_data)

+    @property
+    def key_id(self) -> str | None:
+        return self._jwk_data.get('kid')
+
+    @property
+    def public_key_use(self) -> str | None:
+        return self._jwk_data.get('use')
+
+    @property
+    def key_type(self) -> str:
+        return self._jwk_data.get('kty')
+
+    @classmethod
+    def from_dict(cls, obj: JWKDict, algorithm: str | None=None) -> 'PyJWK':
+        """Creates a PyJWK from a dict object."""
+        if not isinstance(obj, dict):
+            raise InvalidKeyError('Invalid JWK format')
+        return cls(obj, algorithm)
+
+    @classmethod
+    def from_json(cls, data: str, algorithm: str | None=None) -> 'PyJWK':
+        """Creates a PyJWK from a JSON-encoded string."""
+        try:
+            obj = json.loads(data)
+        except ValueError as e:
+            raise InvalidKeyError(f'Invalid JWK format: {str(e)}')
+        return cls.from_dict(obj, algorithm)
+
 class PyJWKSet:

     def __init__(self, keys: list[JWKDict]) -> None:
@@ -71,8 +99,25 @@ class PyJWKSet:
                 return key
         raise KeyError(f'keyset has no key for kid: {kid}')

+    @classmethod
+    def from_dict(cls, obj: dict[str, Any]) -> 'PyJWKSet':
+        """Creates a PyJWKSet from a dict object."""
+        if not isinstance(obj, dict):
+            raise PyJWKSetError('Invalid JWK Set value')
+        keys = obj.get('keys', [])
+        return cls(keys)
+
+    @classmethod
+    def from_json(cls, data: str) -> 'PyJWKSet':
+        """Creates a PyJWKSet from a JSON-encoded string."""
+        try:
+            obj = json.loads(data)
+        except ValueError as e:
+            raise PyJWKSetError(f'Invalid JWK Set value: {str(e)}')
+        return cls.from_dict(obj)
+
 class PyJWTSetWithTimestamp:

-    def __init__(self, jwk_set: PyJWKSet):
+    def __init__(self, jwk_set: PyJWKSet, timestamp: float | None=None):
         self.jwk_set = jwk_set
-        self.timestamp = time.monotonic()
\ No newline at end of file
+        self.timestamp = timestamp if timestamp is not None else time.monotonic()
\ No newline at end of file
diff --git a/jwt/api_jws.py b/jwt/api_jws.py
index 9a20466..84bc60d 100644
--- a/jwt/api_jws.py
+++ b/jwt/api_jws.py
@@ -23,24 +23,38 @@ class PyJWS:
             options = {}
         self.options = {**self._get_default_options(), **options}

+    def _get_default_options(self) -> dict[str, Any]:
+        """Returns the default options for this instance."""
+        return {
+            'verify_signature': True
+        }
+
     def register_algorithm(self, alg_id: str, alg_obj: Algorithm) -> None:
         """
         Registers a new Algorithm for use when creating and verifying tokens.
         """
-        pass
+        if not isinstance(alg_obj, Algorithm):
+            raise TypeError('Algorithm must be an instance of Algorithm')
+        if alg_id in self._algorithms:
+            raise ValueError(f'Algorithm {alg_id} is already registered')
+        self._algorithms[alg_id] = alg_obj
+        self._valid_algs.add(alg_id)

     def unregister_algorithm(self, alg_id: str) -> None:
         """
         Unregisters an Algorithm for use when creating and verifying tokens
         Throws KeyError if algorithm is not registered.
         """
-        pass
+        if alg_id not in self._algorithms:
+            raise KeyError(f'Algorithm {alg_id} not found')
+        del self._algorithms[alg_id]
+        self._valid_algs.remove(alg_id)

     def get_algorithms(self) -> list[str]:
         """
         Returns a list of supported values for the 'alg' parameter.
         """
-        pass
+        return list(self._valid_algs)

     def get_algorithm_by_name(self, alg_name: str) -> Algorithm:
         """
@@ -50,7 +64,9 @@ class PyJWS:

         >>> jws_obj.get_algorithm_by_name("RS256")
         """
-        pass
+        if alg_name not in self._algorithms:
+            raise InvalidAlgorithmError('Algorithm not supported')
+        return self._algorithms[alg_name]

     def get_unverified_header(self, jwt: str | bytes) -> dict[str, Any]:
         """Returns back the JWT header parameters as a dict()
@@ -58,7 +74,277 @@ class PyJWS:
         Note: The signature is not verified so the header parameters
         should not be fully trusted until signature verification is complete
         """
-        pass
+        if not isinstance(jwt, (str, bytes)):
+            raise InvalidTokenError('Invalid token type')
+
+        if isinstance(jwt, str):
+            jwt = jwt.encode('utf-8')
+
+        try:
+            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
+            header_segment, payload_segment = signing_input.split(b'.', 1)
+        except ValueError:
+            raise InvalidTokenError('Not enough segments')
+
+        try:
+            header_data = base64url_decode(header_segment)
+        except (TypeError, binascii.Error):
+            raise DecodeError('Invalid header padding')
+
+        try:
+            header = json.loads(header_data.decode('utf-8'))
+        except ValueError as e:
+            raise DecodeError('Invalid header string: %s' % e)
+
+        if not isinstance(header, dict):
+            raise DecodeError('Invalid header string: must be a json object')
+
+        return header
+
+    def encode(self, payload: bytes, key: str | bytes | AllowedPrivateKeys | None=None, algorithm: str | None=None, headers: dict[str, Any] | None=None, json_encoder: type[json.JSONEncoder] | None=None, is_payload_detached: bool=False, sort_headers: bool=False) -> str:
+        """Creates a JWT using the given algorithm.
+
+        Args:
+            payload: The claims content to sign
+            key: The key to use for signing the claim. Note: if the algorithm is None, the key is not used
+            algorithm: The signing algorithm to use. If none is specified then 'none' is used.
+            headers: A dict of additional headers to use.
+            json_encoder: A custom JSON encoder to use for encoding the JWT.
+            is_payload_detached: If True, the payload will be detached from the JWS.
+            sort_headers: If True, sort the header keys.
+        """
+        # Check that we have a mapping
+        if not isinstance(payload, bytes):
+            raise TypeError('Payload must be bytes')
+
+        if algorithm is None:
+            algorithm = 'none'
+
+        if algorithm not in self._valid_algs:
+            raise InvalidAlgorithmError('Algorithm not supported')
+
+        if algorithm != 'none' and key is None:
+            raise InvalidKeyError('Key is required when algorithm is not "none"')
+
+        # Header
+        header = {'alg': algorithm}
+        if self.header_typ is not None and 'typ' not in (headers or {}):
+            header['typ'] = self.header_typ
+
+        if headers:
+            header.update(headers)
+            if header.get('typ') == '':
+                del header['typ']
+            elif header.get('typ') is None:
+                del header['typ']
+
+        if is_payload_detached:
+            header['b64'] = False
+            if not payload:
+                raise InvalidTokenError('Payload cannot be empty when using detached content')
+
+        if sort_headers:
+            header = dict(sorted(header.items()))
+
+        json_header = json.dumps(header, separators=(',', ':'), cls=json_encoder).encode('utf-8')
+        header_input = base64url_encode(json_header)
+
+        if is_payload_detached:
+            payload_input = b''
+        else:
+            payload_input = base64url_encode(payload)
+
+        signing_input = b'.'.join([header_input, payload_input])
+
+        try:
+            alg_obj = self._algorithms[algorithm]
+            if algorithm == 'none':
+                key = None
+            elif key is None:
+                raise TypeError('Key is required when algorithm is not "none"')
+            else:
+                key = alg_obj.prepare_key(key)
+            signature = alg_obj.sign(signing_input if not is_payload_detached else payload, key)
+        except Exception as e:
+            raise TypeError('Unable to encode JWT: %s' % e)
+
+        encoded_signature = base64url_encode(signature)
+        encoded_jwt = b'.'.join([signing_input, encoded_signature])
+
+        return encoded_jwt.decode('utf-8')
+
+    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> dict[str, Any]:
+        """Decodes a JWT and returns a dict of the token contents.
+
+        Args:
+            jwt: The JWT to decode.
+            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
+            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
+            options: A dict of options for decoding. If None, use default options.
+            detached_payload: The detached payload to use for verification.
+            **kwargs: Additional options for decoding.
+
+        Returns:
+            A dict including:
+                - header: A dict of the JWT header
+                - payload: The decoded payload
+                - signature: The signature of the JWT
+        """
+        deprecated_kwargs = {
+            'verify': 'verify_signature',
+            'verify_exp': 'verify_exp',
+            'verify_iat': 'verify_iat',
+            'verify_nbf': 'verify_nbf',
+            'verify_aud': 'verify_aud',
+            'verify_iss': 'verify_iss',
+        }
+
+        options = options or {}
+        for old_name, new_name in deprecated_kwargs.items():
+            if old_name in kwargs:
+                warnings.warn(
+                    f'The {old_name} parameter is deprecated. '
+                    f'Please use {new_name} in options instead.',
+                    category=DeprecationWarning,
+                    stacklevel=2
+                )
+                options[new_name] = kwargs.pop(old_name)
+
+        for kwarg in kwargs:
+            warnings.warn(
+                f'The "{kwarg}" argument is not supported and will be ignored.',
+                category=RemovedInPyjwt3Warning,
+                stacklevel=2
+            )
+
+        merged_options = {**self.options}
+        if options:
+            if not isinstance(options, dict):
+                raise TypeError('options must be a dict')
+            merged_options.update(options)
+
+        if isinstance(jwt, str):
+            jwt = jwt.encode('utf-8')
+
+        if not isinstance(jwt, bytes):
+            raise DecodeError('Invalid token type')
+
+        try:
+            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
+            header_segment, payload_segment = signing_input.split(b'.', 1)
+        except ValueError:
+            raise InvalidTokenError('Not enough segments')
+
+        try:
+            header_data = base64url_decode(header_segment)
+        except (TypeError, binascii.Error):
+            raise DecodeError('Invalid header padding')
+
+        try:
+            header = json.loads(header_data.decode('utf-8'))
+        except ValueError as e:
+            raise DecodeError('Invalid header string: %s' % e)
+
+        if not isinstance(header, dict):
+            raise DecodeError('Invalid header string: must be a json object')
+
+        if header.get('b64', True):
+            try:
+                payload = base64url_decode(payload_segment)
+            except (TypeError, binascii.Error):
+                raise DecodeError('Invalid payload padding')
+        else:
+            if detached_payload is None:
+                raise DecodeError('It is required that you pass in a value for the "detached_payload" argument to decode a message using unencoded payload.')
+            payload = detached_payload
+
+        try:
+            signature = base64url_decode(crypto_segment)
+        except (TypeError, binascii.Error):
+            raise DecodeError('Invalid crypto padding')
+
+        if algorithms is None:
+            algorithms = list(self._valid_algs)
+
+        if not algorithms and merged_options['verify_signature']:
+            raise DecodeError('No algorithms were specified')
+
+        try:
+            alg = header['alg']
+        except KeyError:
+            raise InvalidTokenError('Missing algorithm ("alg") in headers')
+
+        if alg not in algorithms:
+            raise InvalidAlgorithmError('The specified alg value is not allowed')
+
+        if alg == 'none':
+            if merged_options['verify_signature']:
+                raise DecodeError('Algorithm "none" not allowed')
+            if key not in [None, '', 'none']:
+                raise InvalidKeyError('When alg = "none", key must be empty or "none"')
+            if signature != b'':
+                raise InvalidSignatureError('Signature verification failed')
+            return {
+                'header': header,
+                'payload': payload,
+                'signature': signature
+            }
+
+        try:
+            alg_obj = self._algorithms[alg]
+        except KeyError:
+            raise InvalidAlgorithmError('Algorithm not supported')
+
+        if merged_options['verify_signature']:
+            try:
+                if key is None:
+                    raise InvalidKeyError('Key is required when algorithm is not "none"')
+                key = alg_obj.prepare_key(key)
+            except InvalidKeyError:
+                raise
+            except Exception as e:
+                raise InvalidTokenError('Unable to parse signature key: %s' % e)
+
+            try:
+                if not alg_obj.verify(signing_input if header.get('b64', True) else payload, key, signature):
+                    raise InvalidSignatureError('Signature verification failed')
+            except Exception as e:
+                raise InvalidSignatureError('Signature verification failed: %s' % e)
+        elif key is not None and key not in [None, '', 'none']:
+            try:
+                key = alg_obj.prepare_key(key)
+            except Exception:
+                pass
+
+        if not algorithms and not merged_options['verify_signature']:
+            warnings.warn(
+                'It is required that you pass in a value for the "algorithms" argument when calling decode(). '
+                'This argument will be mandatory in a future version.',
+                category=DeprecationWarning,
+                stacklevel=2
+            )
+
+        if not merged_options['verify_signature'] and not algorithms:
+            warnings.warn(
+                'The "algorithms" argument is not optional when "verify_signature" is False. '
+                'This argument will be mandatory in a future version.',
+                category=DeprecationWarning,
+                stacklevel=2
+            )
+
+        return {
+            'header': header,
+            'payload': payload,
+            'signature': signature
+        }
+
+    def decode(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, detached_payload: bytes | None=None, **kwargs: Any) -> bytes:
+        """Decodes a JWT and returns the payload.
+
+        This is a shortcut to :meth:`decode_complete()` that returns just the payload.
+        """
+        decoded = self.decode_complete(jwt, key, algorithms, options, detached_payload, **kwargs)
+        return decoded['payload']
 _jws_global_obj = PyJWS()
 encode = _jws_global_obj.encode
 decode_complete = _jws_global_obj.decode_complete
diff --git a/jwt/api_jwt.py b/jwt/api_jwt.py
index a61eb5f..75fdc2e 100644
--- a/jwt/api_jwt.py
+++ b/jwt/api_jwt.py
@@ -18,6 +18,18 @@ class PyJWT:
             options = {}
         self.options: dict[str, Any] = {**self._get_default_options(), **options}

+    def _get_default_options(self) -> dict[str, Any]:
+        """Returns the default options for this instance."""
+        return {
+            'verify_signature': True,
+            'verify_exp': True,
+            'verify_nbf': True,
+            'verify_iat': True,
+            'verify_aud': True,
+            'verify_iss': True,
+            'require': []
+        }
+
     def _encode_payload(self, payload: dict[str, Any], headers: dict[str, Any] | None=None, json_encoder: type[json.JSONEncoder] | None=None) -> bytes:
         """
         Encode a given payload to the bytes to be signed.
@@ -25,7 +37,8 @@ class PyJWT:
         This method is intended to be overridden by subclasses that need to
         encode the payload in a different way, e.g. compress the payload.
         """
-        pass
+        json_str = json.dumps(payload, separators=(',', ':'), cls=json_encoder).encode('utf-8')
+        return json_str

     def _decode_payload(self, decoded: dict[str, Any]) -> Any:
         """
@@ -35,7 +48,127 @@ class PyJWT:
         decode the payload in a different way, e.g. decompress compressed
         payloads.
         """
-        pass
+        try:
+            payload = json.loads(decoded['payload'].decode('utf-8'))
+        except ValueError as e:
+            raise DecodeError('Invalid payload string: %s' % e)
+        if not isinstance(payload, dict):
+            raise DecodeError('Invalid payload string: must be a json object')
+        return payload
+
+    def encode(self, payload: dict[str, Any], key: str | bytes | AllowedPrivateKeys | None=None, algorithm: str | None=None, headers: dict[str, Any] | None=None, json_encoder: type[json.JSONEncoder] | None=None) -> str:
+        """
+        Encode a JWT from a payload and optional headers.
+
+        Takes a payload and signs it using the specified algorithm.
+
+        Arguments:
+            payload: A dict of claims for the JWT.
+            key: The key to use for signing the claim. Note: if the algorithm is None, the key is not used.
+            algorithm: The signing algorithm to use. If none is specified then 'none' is used.
+            headers: A dict of additional headers to use.
+            json_encoder: A custom JSON encoder to use for encoding the JWT.
+        """
+        # Check that we have a mapping
+        if not isinstance(payload, dict):
+            raise TypeError('Payload must be a dict')
+
+        # Add reserved claims
+        for time_claim in ['exp', 'iat', 'nbf']:
+            if time_claim in payload:
+                value = payload[time_claim]
+                if isinstance(value, datetime):
+                    payload[time_claim] = timegm(value.utctimetuple())
+                elif isinstance(value, str):
+                    try:
+                        payload[time_claim] = int(value)
+                    except ValueError:
+                        raise TypeError(f'{time_claim} must be a valid timestamp')
+                elif not isinstance(value, (int, float)):
+                    raise TypeError(f'{time_claim} must be a valid timestamp')
+
+        json_payload = self._encode_payload(payload, headers, json_encoder)
+        return api_jws.encode(json_payload, key, algorithm, headers, json_encoder)
+
+    def decode_complete(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, **kwargs: Any) -> dict[str, Any]:
+        """
+        Decodes a JWT and returns a dict of the token contents.
+
+        Args:
+            jwt: The JWT to decode.
+            key: The key to use for verifying the claim. Note: if the algorithm is 'none', the key is not used.
+            algorithms: A list of allowed algorithms. If None, default to the algorithms registered.
+            options: A dict of options for decoding. If None, use default options.
+            **kwargs: Additional options for decoding.
+
+        Returns:
+            A dict including:
+                - header: A dict of the JWT header
+                - payload: The decoded payload
+                - signature: The signature of the JWT
+        """
+        merged_options = {**self.options, **(options or {})}
+        decoded = api_jws.decode_complete(jwt, key, algorithms, merged_options)
+        payload = self._decode_payload(decoded)
+
+        if merged_options['verify_exp'] and 'exp' in payload:
+            now = kwargs.get('now', datetime.now(timezone.utc))
+            exp = datetime.fromtimestamp(payload['exp'], tz=timezone.utc)
+            leeway = timedelta(seconds=kwargs.get('leeway', 0))
+            if now > exp + leeway:
+                raise ExpiredSignatureError('Signature has expired')
+
+        if merged_options['verify_nbf'] and 'nbf' in payload:
+            now = kwargs.get('now', datetime.now(timezone.utc))
+            nbf = datetime.fromtimestamp(payload['nbf'], tz=timezone.utc)
+            leeway = timedelta(seconds=kwargs.get('leeway', 0))
+            if now < nbf - leeway:
+                raise ImmatureSignatureError('The token is not yet valid (nbf)')
+
+        if merged_options['verify_iat'] and 'iat' in payload:
+            now = kwargs.get('now', datetime.now(timezone.utc))
+            iat = datetime.fromtimestamp(payload['iat'], tz=timezone.utc)
+            leeway = timedelta(seconds=kwargs.get('leeway', 0))
+            if now < iat - leeway:
+                raise InvalidIssuedAtError('Issued at claim (iat) cannot be in the future')
+
+        if merged_options['verify_iss']:
+            expected_issuer = kwargs.get('issuer', None)
+            if expected_issuer is not None:
+                if 'iss' not in payload:
+                    raise MissingRequiredClaimError('Issuer claim expected but not present')
+                if payload['iss'] != expected_issuer:
+                    raise InvalidIssuerError('Invalid issuer')
+
+        if merged_options['verify_aud']:
+            expected_audience = kwargs.get('audience', None)
+            if expected_audience is not None:
+                if 'aud' not in payload:
+                    raise MissingRequiredClaimError('Audience claim expected but not present')
+                audience = payload['aud']
+                if isinstance(audience, str):
+                    audience = [audience]
+                if not isinstance(audience, Iterable) or not all(isinstance(aud, str) for aud in audience):
+                    raise InvalidAudienceError('Invalid claim format in token')
+                if expected_audience not in audience:
+                    raise InvalidAudienceError('Invalid audience')
+
+        if merged_options['require']:
+            for claim in merged_options['require']:
+                if claim not in payload:
+                    raise MissingRequiredClaimError(f'Token is missing the "{claim}" claim')
+
+        decoded['payload'] = payload
+        return decoded
+
+    def decode(self, jwt: str | bytes, key: str | bytes | AllowedPublicKeys | None=None, algorithms: list[str] | None=None, options: dict[str, Any] | None=None, **kwargs: Any) -> dict[str, Any]:
+        """
+        Decodes a JWT and returns the payload.
+
+        This is a shortcut to :meth:`decode_complete()` that returns just the payload.
+        """
+        decoded = self.decode_complete(jwt, key, algorithms, options, **kwargs)
+        return decoded['payload']
 _jwt_global_obj = PyJWT()
 encode = _jwt_global_obj.encode
 decode_complete = _jwt_global_obj.decode_complete
diff --git a/jwt/jwk_set_cache.py b/jwt/jwk_set_cache.py
index e57abb8..87cba70 100644
--- a/jwt/jwk_set_cache.py
+++ b/jwt/jwk_set_cache.py
@@ -6,4 +6,22 @@ class JWKSetCache:

     def __init__(self, lifespan: int) -> None:
         self.jwk_set_with_timestamp: Optional[PyJWTSetWithTimestamp] = None
-        self.lifespan = lifespan
\ No newline at end of file
+        self.lifespan = lifespan
+
+    @property
+    def jwk_set(self) -> PyJWKSet:
+        if self.jwk_set_with_timestamp is None:
+            raise ValueError('No JWK set has been cached')
+        return self.jwk_set_with_timestamp.jwk_set
+
+    @jwk_set.setter
+    def jwk_set(self, value: PyJWKSet) -> None:
+        self.jwk_set_with_timestamp = PyJWTSetWithTimestamp(value, int(time.time()))
+
+    def is_expired(self) -> bool:
+        if self.jwk_set_with_timestamp is None:
+            return True
+        return int(time.time()) - self.jwk_set_with_timestamp.timestamp > self.lifespan
+
+    def delete(self) -> None:
+        self.jwk_set_with_timestamp = None
\ No newline at end of file
diff --git a/jwt/jwks_client.py b/jwt/jwks_client.py
index 34c461f..a717851 100644
--- a/jwt/jwks_client.py
+++ b/jwt/jwks_client.py
@@ -26,4 +26,101 @@ class PyJWKClient:
         else:
             self.jwk_set_cache = None
         if cache_keys:
-            self.get_signing_key = lru_cache(maxsize=max_cached_keys)(self.get_signing_key)
\ No newline at end of file
+            self.get_signing_key = lru_cache(maxsize=max_cached_keys)(self.get_signing_key)
+
+    def fetch_data(self) -> str:
+        """Fetch the JWKS data from the uri provided during instantiation."""
+        try:
+            request = urllib.request.Request(self.uri, headers=self.headers)
+            if self.ssl_context:
+                response = urllib.request.urlopen(request, timeout=self.timeout, context=self.ssl_context)
+            else:
+                response = urllib.request.urlopen(request, timeout=self.timeout)
+            data = response.read()
+            if isinstance(data, str):
+                return data
+            return data.decode('utf-8')
+        except URLError as e:
+            raise PyJWKClientConnectionError(f'Failed to fetch JWKS from {self.uri}. Error: {str(e)}')
+
+    def get_jwk_set(self, refresh: bool=False) -> PyJWKSet:
+        """Return the fetched PyJWKSet.
+        
+        Args:
+            refresh: Force a refetch of the JWKS.
+        """
+        if not refresh and self.jwk_set_cache and not self.jwk_set_cache.is_expired():
+            return self.jwk_set_cache.jwk_set
+
+        data = self.fetch_data()
+        try:
+            jwk_set = PyJWKSet.from_json(data)
+        except Exception as e:
+            if self.jwk_set_cache:
+                self.jwk_set_cache.delete()
+            raise PyJWKClientError(f'Failed to parse JWKS: {str(e)}')
+
+        if self.jwk_set_cache:
+            self.jwk_set_cache.jwk_set = jwk_set
+
+        return jwk_set
+
+    def get_signing_keys(self) -> List[PyJWK]:
+        """Return a list of signing keys from the JWKS."""
+        jwk_set = self.get_jwk_set()
+        signing_keys = []
+
+        for jwk_key in jwk_set.keys:
+            if jwk_key.public_key_use == 'sig' or not jwk_key.public_key_use:
+                signing_keys.append(jwk_key)
+
+        if not signing_keys:
+            raise PyJWKClientError('No signing keys found in JWKS')
+
+        return signing_keys
+
+    def get_signing_key(self, kid: str) -> PyJWK:
+        """Return the signing key from the JWKS that matches the provided kid.
+        
+        Args:
+            kid: The key ID to search for.
+        """
+        signing_keys = self.get_signing_keys()
+        for key in signing_keys:
+            if key.key_id == kid:
+                return key
+
+        # If no key is found, try refreshing the JWKS once
+        signing_keys = self.get_signing_keys()
+        for key in signing_keys:
+            if key.key_id == kid:
+                return key
+
+        raise PyJWKClientError(f'Unable to find a signing key that matches: {kid}')
+
+    def get_signing_key_from_jwt(self, token: str, refresh_jwks: bool=True) -> PyJWK:
+        """Return the signing key from the JWKS that matches the kid in the token header.
+        
+        Args:
+            token: The JWT token to get the key for.
+            refresh_jwks: Whether to refresh the JWKS if the key is not found.
+        """
+        try:
+            headers = decode_token(token, options={'verify_signature': False})['header']
+        except Exception as e:
+            raise PyJWKClientError(f'Failed to decode JWT headers: {str(e)}')
+
+        kid = headers.get('kid')
+        if not kid:
+            signing_keys = self.get_signing_keys()
+            if len(signing_keys) == 1:
+                return signing_keys[0]
+            raise PyJWKClientError('Token headers must include a key ID ("kid")')
+
+        try:
+            return self.get_signing_key(kid)
+        except PyJWKClientError:
+            if refresh_jwks:
+                self.get_jwk_set(refresh=True)
+                return self.get_signing_key(kid)
+            raise
\ No newline at end of file
diff --git a/jwt/utils.py b/jwt/utils.py
index 3115d4c..6b01bd9 100644
--- a/jwt/utils.py
+++ b/jwt/utils.py
@@ -7,6 +7,74 @@ try:
     from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature
 except ModuleNotFoundError:
     pass
+
+def force_bytes(value: Union[str, bytes]) -> bytes:
+    if isinstance(value, str):
+        return value.encode('utf-8')
+    elif isinstance(value, bytes):
+        return value
+    else:
+        raise TypeError('Expected string or bytes type')
+
+def base64url_decode(input: Union[str, bytes]) -> bytes:
+    if isinstance(input, str):
+        input = input.encode('ascii')
+    
+    rem = len(input) % 4
+    if rem > 0:
+        input += b'=' * (4 - rem)
+    
+    return base64.urlsafe_b64decode(input)
+
+def base64url_encode(input: bytes) -> bytes:
+    return base64.urlsafe_b64encode(input).rstrip(b'=')
+
+def to_base64url_uint(val: int) -> bytes:
+    if val < 0:
+        raise ValueError('Must be a positive integer')
+    
+    if val == 0:
+        return b'AA'
+    
+    int_bytes = val.to_bytes((val.bit_length() + 7) // 8, byteorder='big')
+    return base64url_encode(int_bytes)
+
+def from_base64url_uint(val: Union[str, bytes]) -> int:
+    if isinstance(val, str):
+        val = val.encode('ascii')
+    
+    data = base64url_decode(val)
+    return int.from_bytes(data, byteorder='big')
+
+def der_to_raw_signature(der_sig: bytes, curve: EllipticCurve) -> bytes:
+    r, s = decode_dss_signature(der_sig)
+    key_size = (curve.key_size + 7) // 8
+    return r.to_bytes(key_size, byteorder='big') + s.to_bytes(key_size, byteorder='big')
+
+def raw_to_der_signature(raw_sig: bytes, curve: EllipticCurve) -> bytes:
+    key_size = (curve.key_size + 7) // 8
+    if len(raw_sig) != 2 * key_size:
+        raise ValueError('Invalid signature')
+    
+    r = int.from_bytes(raw_sig[:key_size], byteorder='big')
+    s = int.from_bytes(raw_sig[key_size:], byteorder='big')
+    return encode_dss_signature(r, s)
+
+def is_pem_format(key: bytes) -> bool:
+    return bool(_PEM_RE.search(key))
+
+def is_ssh_key(key: bytes) -> bool:
+    if key.startswith(b'ssh-') or key.startswith(b'ecdsa-'):
+        return True
+    
+    match = _SSH_PUBKEY_RC.match(key)
+    if not match:
+        return False
+    
+    key_type = match.group(1)
+    return (key_type in _SSH_KEY_FORMATS or 
+            any(key_type.endswith(suffix) for suffix in [_CERT_SUFFIX]))
+
 _PEMS = {b'CERTIFICATE', b'TRUSTED CERTIFICATE', b'PRIVATE KEY', b'PUBLIC KEY', b'ENCRYPTED PRIVATE KEY', b'OPENSSH PRIVATE KEY', b'DSA PRIVATE KEY', b'RSA PRIVATE KEY', b'RSA PUBLIC KEY', b'EC PRIVATE KEY', b'DH PARAMETERS', b'NEW CERTIFICATE REQUEST', b'CERTIFICATE REQUEST', b'SSH2 PUBLIC KEY', b'SSH2 ENCRYPTED PRIVATE KEY', b'X509 CRL'}
 _PEM_RE = re.compile(b'----[- ]BEGIN (' + b'|'.join(_PEMS) + b')[- ]----\r?\n.+?\r?\n----[- ]END \\1[- ]----\r?\n?', re.DOTALL)
 _CERT_SUFFIX = b'-cert-v01@openssh.com'