Error `TypeError: ECPublicKey.verify() takes 3 positional arguments but 4 were given` while using `jwt.decode`
Getting this error TypeError: ECPublicKey.verify() takes 3 positional arguments but 4 were given while trying to decode a token with RS256 algorithm.
Expected Result
Decode some token via jwt.decode
Actual Result
... (omitted) ...
File /opt/venv/lib/python3.12/site-packages/jwt/api_jwt.py:210, in PyJWT.decode(self, jwt, key, algorithms, options, verify, detached_payload, audience, issuer, leeway, **kwargs)
203 if kwargs:
204 warnings.warn(
205 "passing additional kwargs to decode() is deprecated "
206 "and will be removed in pyjwt version 3. "
207 f"Unsupported kwargs: {tuple(kwargs.keys())}",
208 RemovedInPyjwt3Warning,
209 )
--> 210 decoded = self.decode_complete(
211 jwt,
212 key,
213 algorithms,
214 options,
215 verify=verify,
216 detached_payload=detached_payload,
217 audience=audience,
218 issuer=issuer,
219 leeway=leeway,
220 )
221 return decoded["payload"]
File /opt/venv/lib/python3.12/site-packages/jwt/api_jwt.py:151, in PyJWT.decode_complete(self, jwt, key, algorithms, options, verify, detached_payload, audience, issuer, leeway, **kwargs)
146 if options["verify_signature"] and not algorithms:
147 raise DecodeError(
148 'It is required that you pass in a value for the "algorithms" argument when calling decode().'
149 )
--> 151 decoded = api_jws.decode_complete(
152 jwt,
153 key=key,
154 algorithms=algorithms,
155 options=options,
156 detached_payload=detached_payload,
157 )
159 payload = self._decode_payload(decoded)
161 merged_options = {**self.options, **options}
File /opt/venv/lib/python3.12/site-packages/jwt/api_jws.py:209, in PyJWS.decode_complete(self, jwt, key, algorithms, options, detached_payload, **kwargs)
206 signing_input = b".".join([signing_input.rsplit(b".", 1)[0], payload])
208 if verify_signature:
--> 209 self._verify_signature(signing_input, header, signature, key, algorithms)
211 return {
212 "payload": payload,
213 "header": header,
214 "signature": signature,
215 }
File /opt/venv/lib/python3.12/site-packages/jwt/api_jws.py:309, in PyJWS._verify_signature(self, signing_input, header, signature, key, algorithms)
306 raise InvalidAlgorithmError("Algorithm not supported") from e
307 prepared_key = alg_obj.prepare_key(key)
--> 309 if not alg_obj.verify(signing_input, prepared_key, signature):
310 raise InvalidSignatureError("Signature verification failed")
File /opt/venv/lib/python3.12/site-packages/jwt/algorithms.py:483, in RSAAlgorithm.verify(self, msg, key, sig)
481 def verify(self, msg: bytes, key: RSAPublicKey, sig: bytes) -> bool:
482 try:
--> 483 key.verify(sig, msg, padding.PKCS1v15(), self.hash_alg())
484 return True
485 except InvalidSignature:
TypeError: ECPublicKey.verify() takes 3 positional arguments but 4 were given
Reproduction Steps
import jwt
token = "..."
key = "..."
jwt.decode(
jwt=token,
key=key
algorithms=["RS256",]
)
System Information
$ python -m jwt.help
{
"cryptography": {
"version": "42.0.5"
},
"implementation": {
"name": "CPython",
"version": "3.12.4"
},
"platform": {
"release": "5.15.0-113-generic",
"system": "Linux"
},
"pyjwt": {
"version": "2.8.0"
}
}
Exact same problem in 2.9.0.
{
"cryptography": {
"version": "43.0.1"
},
"implementation": {
"name": "CPython",
"version": "3.12.4"
},
"platform": {
"release": "11",
"system": "Windows"
},
"pyjwt": {
"version": "2.9.0"
}
}
Can you check what type(load_pem_public_key(your_key.encode("utf-8"))is? It seems to be returning an ElliptiveCurvePublicKey.
>>> from cryptography.hazmat.primitives.serialization import load_pem_public_key
>>> your_key = "..." # unicode string, or you if you already have it as bytes skip the encode() below
>>> type(load_pem_public_key(your_key.encode("utf-8")))
(if load_pem_public_key throws an error for you, try load_ssh_public_key - that is, make sure you are using the loader relevant to how the key is formatted)
Yes, my problem was caused because I was taking the wrong key from the JWKS endpoint of my openid-configuration. I was taking the first one that it is in fact an elliptic curve key ({"kty": "EC"}).
Using a PyJWTClient and the method get_signing_key_from_jwt as it is showed in the example https://pyjwt.readthedocs.io/en/latest/usage.html#oidc-login-flow it took the correct key and decoded without issues.
I do feel like we should catch these issues where the key passed in is from the wrong algo-family (and, ideally, down to ensuring the same algo was used). I can attempt a PR this week if that's the approach we want to take.
Yes, maybe it should warn you (or throw you) in some way that you are trying to verify with an ECPublicKey when the algorithm you specified is RS256.
But if we are already detecting the key algorithm and we know the algorithm passed by the user, we could catch the error and be more specific.
Only my two cents as an user, I'm by no means an expert in this. I'm glad to help if needed.
Draft PR, so we have something to look at while proposing a "fix" for this.
This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days