python-jose implements JWS, JWE, JWK and JWT with pluggable backends — pure-Python ecdsa, pycryptodome or cryptography; the check installed the cryptography extra. Years of FastAPI and Flask tutorials made it widespread, but development is slow: 3.4.0 (February 2025) came almost four years after 3.3.0, and new projects usually choose PyJWT, jwcrypto or joserfc instead.
algorithms is optional in jwt.decode(), and without it the algorithm comes from the token header. Combined with advisory GHSA-3qf3-8w2g-rqmx (CVE-2026-85394), published in September 2026 and unfixed in 3.5.0, that is exploitable: the guard against using public keys as HMAC secrets recognises PEM and OpenSSH text, but not the same key as DER bytes. The check reproduced it — an HS256 token whose HMAC key was the DER public key verified against that key when algorithms was left out, and was rejected with The specified alg value is not allowed once algorithms=["ES256"] was passed. Always pass algorithms; keep public keys as PEM strings; and check secrets with the JWT secret checker.
The algorithm set is the oldest of the Python libraries: the HS, RS and ES families only. PS256, EdDSA and ES256K raise Algorithm … not supported., so tokens from providers that switched to Ed25519 or RSA-PSS cannot be verified. ES256 is not bound to P-256 either: a P-384 key produced a 96-byte signature under an ES256 header that python-jose verified but other libraries, such as jose and PyJWT, refuse.
ES256 signatures are 64 bytes of raw r‖s and a DER signature fails with Signature verification failed.; the JWT debugger shows which form a token carries. exp is optional and leeway is zero unless you set require_exp and leeway in options, as the sample does.
Unsigned tokens were rejected with Unable to find an algorithm for key, even with algorithms=["none"].