-
-
Notifications
You must be signed in to change notification settings - Fork 739
Description
First of all, thanks for pyjwt!
I'm implementing the consumer side of an OIDC flow, with the verification keys being retrieved periodically from the OIDC provider's JSON Web Key (JWK) set. I then turn each JWK's contents into this library's PyJWK type.
Expected Result
For verification, I'm doing something like this:
signed_payload = jwt.decode(
unverified_token,
key=key,
algorithms=["RS256"],
options=dict(
verify_signature=True,
# "require" only checks for the presence of these claims, not
# their validity. Each has a corresponding "verify_" kwarg
# that enforces their actual validity.
require=["iss", "iat", "nbf", "exp", "aud"],
verify_iss=True,
verify_iat=True,
verify_nbf=True,
verify_exp=True,
verify_aud=True,
),
issuer=self.issuer_url,
audience=self.audience,
leeway=30,
)...where key is a PyJWK.
I expect this to succeed and return the signed claim dictionary (assuming key is valid).
Actual Result
Instead, I get a TypeError with the following message: Expecting a PEM-formatted key.
I traced this down to algorithms.py, where the key is expected to be in PEM format for RSA keys:
...which made me check the types for the jwt.decode API, where I realized that this wasn't supported to begin with 😅
Lines 182 to 198 in 777efa2
| def decode( | |
| self, | |
| jwt: str | bytes, | |
| key: str | bytes = "", | |
| algorithms: Optional[List[str]] = None, | |
| options: Optional[Dict[str, Any]] = None, | |
| # deprecated arg, remove in pyjwt3 | |
| verify: Optional[bool] = None, | |
| # could be used as passthrough to api_jws, consider removal in pyjwt3 | |
| detached_payload: Optional[bytes] = None, | |
| # passthrough arguments to _validate_claims | |
| # consider putting in options | |
| audience: Optional[Union[str, Iterable[str]]] = None, | |
| issuer: Optional[str] = None, | |
| leeway: Union[int, float, timedelta] = 0, | |
| # kwargs | |
| **kwargs, |
Request
So, my feature request: could jwt.decode accept PyJWK instances for the key= parameter?
I think most of the scaffolding for this is already in place: the Algorithm ABC already has a from_jwk classmethod, so each concrete implementation could use it in its concrete prepare_key implementation. From there, the top-level signature(s) could be broadened from str | bytes to str | bytes | PyJWK.
Alternatives considered
Given that the top level decode API only accepts PEM-encoded str or bytes objects, I'm not sure if there's a clean alternative to this.
I could do something hacky and manually re-encoded the PyJWK as a PEM-encoded key, potentially with the help of the cryptography APIs. But that seems error prone 🙂