Skip to content

very unhelpfull / missleading error messages on get_signing_key_from_jwt #820

@julianhahn

Description

@julianhahn

Hello,

I spend a few hours today for a bug that shouldn't have been there. I set up my python application where I recieve a jwt token from oauth-proxy, validate, decode and echo the contents.
Running locally, everything went perfect. Running in a container I got the message "The JWK Set did not contain any usable keys". I checked, oauth configs, credentials, my code, put a few hours into trying to debug the python code while running in podman.

Then after a few hours my colleague helped and ran the project on his machine. He got the same error.
So what does his machine has not, that mine has? Dependencies.

After another hour of debuging, we found out, that my jwt token is encoded with RSA. For pyjwt to decode the key we need to use RSA. To use RSA the cryptography is nessecary. I got this installed in my global dependencies and forgot to use an env.

The thing is:
Alls this could have been prevented, if the raised error message would be "found RSA - cannot decode because dependency cryptography is missing"

Instead the error handling is as follows:

api_jwk.py - PyJWKSet:

class PyJWKSet:
    def __init__(self, keys: list[dict]) -> None:
        self.keys = []

        if not keys:
            raise PyJWKSetError("The JWK Set did not contain any keys")

        if not isinstance(keys, list):
            raise PyJWKSetError("Invalid JWK Set value")

        for key in keys:
            try:
                *self.keys.append(PyJWK(key))*
            *except PyJWKError:*
                # skip unusable keys
                *continue*

        if len(self.keys) == 0:
            raise PyJWKSetError("The JWK Set did not contain any usable keys")

->

class PyJWK:
    def __init__(self, jwk_data, algorithm=None):
        self._algorithms = get_default_algorithms()

->

ef get_default_algorithms():
    """
    Returns the algorithms that are implemented by the library.
    """
    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),
   ...

and has_crypto comes from algorithms.py

try:
    import cryptography.exceptions
....
except ModuleNotFoundError:
    has_crypto = False

As summary:
You iterate through every key, looking if there is an algorithm, find that a dependecie is missing and skip this key, and buring the error message. Afterwards the length of the list of keys is 0 - you get the message of no usable keys and are none the wiser, why this is the case.
please rework you try catch error flow, so that a developer knows when a dependency is missing for the current key!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions