Changelog
All notable changes to this project will be documented in this file. This project adheres to Semantic Versioning.
Unreleased
Fixed
Added
v2.11.0
Fixed
Enforce ECDSA curve validation per RFC 7518 Section 3.4.
Fix build system warnings by @kurtmckee in #1105
Validate key against allowed types for Algorithm family in #964
Add iterator for JWKSet in #1041
Validate iss claim is a string during encoding and decoding by @pachewise in #1040
Improve typing/logic for options in decode, decode_complete by @pachewise in #1045
Declare float supported type for lifespan and timeout by @nikitagashkov in #1068
Fix
SyntaxWarnings/DeprecationWarnings caused by invalid escape sequences by @kurtmckee in #1103Development: Build a shared wheel once to speed up test suite setup times by @kurtmckee in #1114
Development: Test type annotations across all supported Python versions, increase the strictness of the type checking, and remove the mypy pre-commit hook by @kurtmckee in #1112
Added
Support Python 3.14, and test against PyPy 3.10 and 3.11 by @kurtmckee in #1104
Development: Migrate to
buildto test package building in CI by @kurtmckee in #1108Development: Improve coverage config and eliminate unused test suite code by @kurtmckee in #1115
Docs: Standardize CHANGELOG links to PRs by @kurtmckee in #1110
Docs: Fix Read the Docs builds by @kurtmckee in #1111
Docs: Add example of using leeway with nbf by @djw8605 in #1034
Docs: Refactored docs with
autodoc; addedPyJWSandjwt.algorithmsdocs by @pachewise in #1045Docs: Documentation improvements for “sub” and “jti” claims by @cleder in #1088
Development: Add pyupgrade as a pre-commit hook by @kurtmckee in #1109
Add minimum key length validation for HMAC and RSA keys (CWE-326). Warns by default via
InsecureKeyLengthWarningwhen keys are below minimum recommended lengths per RFC 7518 Section 3.2 (HMAC) and NIST SP 800-131A (RSA). Passenforce_minimum_key_length=Truein options toPyJWTorPyJWSto raiseInvalidKeyErrorinstead.Refactor
PyJWTto own an internalPyJWSinstance instead of calling globalapi_jwsfunctions.
v2.10.1
Fixed
Prevent partial matching of iss claim by @fabianbadoi in GHSA-75c5-xw7c-p5pm
v2.10.0
Changed
Remove algorithm requirement from JWT API, instead relying on JWS API for enforcement, by @luhn in #975
Use
Sequencefor parameter types rather thanListwhere applicable by @imnotjames in #970Add JWK support to JWT encode by @luhn in #979
Encoding and decoding payloads using the none algorithm by @jpadilla in #c2629f6
Before:
>>> import jwt >>> jwt.encode({"payload": "abc"}, key=None, algorithm=None)
After:
>>> import jwt >>> jwt.encode({"payload": "abc"}, key=None, algorithm="none")
Added validation for ‘sub’ (subject) and ‘jti’ (JWT ID) claims in tokens by @Divan009 in #1005
Refactor project configuration files from
setup.cfgtopyproject.tomlby @cleder in #995Ruff linter and formatter changes by @gagandeepp in #1001
Drop support for Python 3.8 (EOL) by @kkirsche in #1007
Fixed
Encode EC keys with a fixed bit length by @etianen in #990
Add an RTD config file to resolve Read the Docs build failures by @kurtmckee in #977
Docs: Update
iatexception docs by @pachewise in #974Docs: Fix
decode_completescope and algorithms by @RbnRncn in #982Fix doctest for
docs/usage.rstby @pachewise in #986Fix
test_utils.pynot to xfail by @pachewise in #987Docs: Correct jwt.decode audience param doc expression by @peter279k in #994
Added
Add support for python 3.13 by @hugovk in #972
Create SECURITY.md by @auvipy and @jpadilla in #973
Docs: Add PS256 encoding and decoding usage by @peter279k in #992
Docs: Add API docs for PyJWK by @luhn in #980
Docs: Add EdDSA algorithm encoding/decoding usage by @peter279k in #993
Include checkers and linters for
pyproject.tomlinpre-commitby @cleder in #1002Docs: Add ES256 decoding usage by @Gautam-Hegde in #1003
v2.9.0
Changed
Fixed
Added
Add support for Python 3.12 by @hugovk in #910
Improve performance of
is_ssh_key+ add unit test by @bdraco in #940Allow
jwt.decode()to accept a PyJWK object by @luhn in #886Make
algorithm_nameattribute available on PyJWK by @luhn in #886Raise
InvalidKeyErroron invalid PEM keys to be compatible with cryptography 42.x.x by @CollinEMac in #952Raise an exception when required cryptography dependency is missing by @tobloef in https://github.com/jpadilla/pyjwt/pull/963
v2.8.0
Changed
Update python version test matrix by @auvipy in #895
Fixed
Added
v2.7.0
Changed
Fixed
Added
Add
compute_hash_digestas a method ofAlgorithmobjects, which uses the underlying hash algorithm to compute a digest. If there is no appropriate hash algorithm, aNotImplementedErrorwill be raised in #775Add optional
headersargument toPyJWKClient. If provided, the headers will be included in requests that the client uses when fetching the JWK set by @thundercat1 in #823Add PyJWT._{de,en}code_payload hooks by @akx in #829
Add sort_headers parameter to api_jwt.encode by @evroon in #832
Make mypy configuration stricter and improve typing by @akx in #830
Add more types by @Viicos in #843
Add a timeout for PyJWKClient requests by @daviddavis in #875
Add client connection error exception by @daviddavis in #876
Add complete types to take all allowed keys into account by @Viicos in #873
Add as_dict option to Algorithm.to_jwk by @fluxth in #881
v2.6.0
Changed
Fixed
Added
Adding validation for issued_at when iat > (now + leeway) as ImmatureSignatureError by @sriharan16 in #794
v2.5.0
Changed
Skip keys with incompatible alg when loading JWKSet by @DaGuich in #762
Remove support for python3.6 by @sirosen in #777
Emit a deprecation warning for unsupported kwargs by @sirosen in #776
Remove redundant wheel dep from pyproject.toml by @mgorny in #765
Do not fail when an unusable key occurs by @DaGuich in #762
Update audience typing by @JulianMaurin in #782
Improve PyJWKSet error accuracy by @JulianMaurin in #786
Mypy as pre-commit check + api_jws typing by @JulianMaurin in #787
Fixed
Adjust expected exceptions in option merging tests for PyPy3 by @mgorny in #763
Fixes for pyright on strict mode by @brandon-leapyear in #747
docs: fix simple typo, iinstance -> isinstance by @timgates42 in #774
Fix typo: priot -> prior by @jdufresne in #780
Fix for headers disorder issue by @kadabusha in #721
Added
v2.4.0
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
Fixed
Added
Add support for Python 3.10 by @hugovk in #699
api_jwk: Add PyJWKSet.__getitem__ by @woodruffw in #725
Update usage.rst by @guneybilen in #727
Docs: mention performance reasons for reusing RSAPrivateKey when encoding by @dmahr1 in #734
Fixed typo in usage.rst by @israelabraham in #738
Add detached payload support for JWS encoding and decoding by @fviard in #723
Replace various string interpolations with f-strings by @akx in #744
Update CHANGELOG.rst by @hipertracker in #751
v2.3.0
Fixed
Revert “Remove arbitrary kwargs.” #701
Added
Add exception chaining #702
v2.2.0
Changed
Fixed
Added
Add support for Ed448/EdDSA. #675
v2.1.0
Changed
Allow claims validation without making JWT signature validation mandatory. #608
Fixed
Added
v2.0.1
Changed
Rename CHANGELOG.md to CHANGELOG.rst and include in docs #597
Fixed
Fix from_jwk() for all algorithms #598
Added
v2.0.0
Changed
Drop support for Python 2 and Python 3.0-3.5
Python 3.5 is EOL so we decide to drop its support. Version 1.7.1 is
the last one supporting Python 3.0-3.5.
Require cryptography >= 3
Drop support for PyCrypto and ECDSA
We’ve kept this around for a long time, mostly for environments that didn’t allow installing cryptography.
Drop CLI
Dropped the included cli entry point.
Improve typings
We no longer need to use mypy Python 2 compatibility mode (comments)
jwt.encode(...) return type
Tokens are returned as string instead of a byte string
Dropped deprecated errors
Removed ExpiredSignature, InvalidAudience, and
InvalidIssuer. Use ExpiredSignatureError,
InvalidAudienceError, and InvalidIssuerError instead.
Dropped deprecated verify_expiration param in jwt.decode(...)
Use
jwt.decode(encoded, key, algorithms=["HS256"], options={"verify_exp": False})
instead.
Dropped deprecated verify param in jwt.decode(...)
Use jwt.decode(encoded, key, options={"verify_signature": False})
instead.
Require explicit algorithms in jwt.decode(...) by default
Example: jwt.decode(encoded, key, algorithms=["HS256"]).
Dropped deprecated require_* options in jwt.decode(...)
For example, instead of
jwt.decode(encoded, key, algorithms=["HS256"], options={"require_exp": True}),
use
jwt.decode(encoded, key, algorithms=["HS256"], options={"require": ["exp"]}).
And the old v1.x syntax
jwt.decode(token, verify=False)
is now:
jwt.decode(jwt=token, key='secret', algorithms=['HS256'], options={"verify_signature": False})
Added
Introduce better experience for JWKs
Introduce PyJWK, PyJWKSet, and PyJWKClient.
import jwt
from jwt import PyJWKClient
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRdyJ9.eyJpc3MiOiJodHRwczovL2Rldi04N2V2eDlydS5hdXRoMC5jb20vIiwic3ViIjoiYVc0Q2NhNzl4UmVMV1V6MGFFMkg2a0QwTzNjWEJWdENAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vZXhwZW5zZXMtYXBpIiwiaWF0IjoxNTcyMDA2OTU0LCJleHAiOjE1NzIwMDY5NjQsImF6cCI6ImFXNENjYTc5eFJlTFdVejBhRTJINmtEME8zY1hCVnRDIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.PUxE7xn52aTCohGiWoSdMBZGiYAHwE5FYie0Y1qUT68IHSTXwXVd6hn02HTah6epvHHVKA2FqcFZ4GGv5VTHEvYpeggiiZMgbxFrmTEY0csL6VNkX1eaJGcuehwQCRBKRLL3zKmA5IKGy5GeUnIbpPHLHDxr-GXvgFzsdsyWlVQvPX2xjeaQ217r2PtxDeqjlf66UYl6oY6AqNS8DH3iryCvIfCcybRZkc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA"
kid = "NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw"
url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"
jwks_client = PyJWKClient(url)
signing_key = jwks_client.get_signing_key_from_jwt(token)
data = jwt.decode(
token,
signing_key.key,
algorithms=["RS256"],
audience="https://expenses-api",
options={"verify_exp": False},
)
print(data)
Support for JWKs containing ECDSA keys
Add support for Ed25519 / EdDSA
Pull Requests
Add PyPy3 to the test matrix (#550) by @jdufresne
Require tweak (#280) by @psafont
Decode return type is dict[str, Any] (#393) by @jacopofar
Fix linter error in test_cli (#414) by @jaraco
Run mypy with tox (#421) by @jpadilla
Document (and prefer) pyjwt[crypto] req format (#426) by @gthb
Correct type for json_encoder argument (#438) by @jdufresne
Prefer https:// links where available (#439) by @jdufresne
Pass python_requires argument to setuptools (#440) by @jdufresne
Rename [wheel] section to [bdist_wheel] as the former is legacy (#441) by @jdufresne
Remove setup.py test command in favor of pytest and tox (#442) by @jdufresne
Fix mypy errors (#449) by @jpadilla
DX Tweaks (#450) by @jpadilla
Add support of python 3.8 (#452) by @Djailla
Fix 406 (#454) by @justinbaur
Add support for Ed25519 / EdDSA, with unit tests (#455) by @Someguy123
Remove Python 2.7 compatibility (#457) by @Djailla
Fix simple typo: encododed -> encoded (#462) by @timgates42
Enhance tracebacks. (#477) by @JulienPalard
Simplify
python_requires(#478) by @michael-kDocument top-level .encode and .decode to close #459 (#482) by @dimaqq
Improve documentation for audience usage (#484) by @CorreyL
Correct README on how to run tests locally (#489) by @jdufresne
Fix
tox -e lintwarnings and errors (#490) by @jdufresneRun pyupgrade across project to use modern Python 3 conventions (#491) by @jdufresne
Add Python-3-only trove classifier and remove “universal” from wheel (#492) by @jdufresne
Emit warnings about user code, not pyjwt code (#494) by @mgedmin
Move setup information to declarative setup.cfg (#495) by @jdufresne
CLI options for verifying audience and issuer (#496) by @GeoffRichards
Specify the target Python version for mypy (#497) by @jdufresne
Remove unnecessary compatibility shims for Python 2 (#498) by @jdufresne
Setup GH Actions (#499) by @jpadilla
Implementation of ECAlgorithm.from_jwk (#500) by @jpadilla
Remove cli entry point (#501) by @jpadilla
Expose InvalidKeyError on jwt module (#503) by @russellcardullo
Avoid loading token twice in pyjwt.decode (#506) by @CaselIT
Default links to stable version of documentation (#508) by @salcedo
Update README.md badges (#510) by @jpadilla
Introduce better experience for JWKs (#511) by @jpadilla
Fix tox conditional extras (#512) by @jpadilla
Return tokens as string not bytes (#513) by @jpadilla
Drop support for legacy contrib algorithms (#514) by @jpadilla
Drop deprecation warnings (#515) by @jpadilla
Update Auth0 sponsorship link (#519) by @Sambego
Update return type for jwt.encode (#521) by @moomoolive
Run tests against Python 3.9 and add trove classifier (#522) by @michael-k
Removed redundant
default_backend()(#523) by @rohitkg98Documents how to use private keys with passphrases (#525) by @rayluo
Update version to 2.0.0a1 (#528) by @jpadilla
Fix usage example (#530) by @nijel
add EdDSA to docs (#531) by @CircleOnCircles
Remove support for EOL Python 3.5 (#532) by @jdufresne
Upgrade to isort 5 and adjust configurations (#533) by @jdufresne
Remove unused argument “verify” from PyJWS.decode() (#534) by @jdufresne
Update typing syntax and usage for Python 3.6+ (#535) by @jdufresne
Run pyupgrade to simplify code and use Python 3.6 syntax (#536) by @jdufresne
Drop unknown pytest config option: strict (#537) by @jdufresne
Upgrade black version and usage (#538) by @jdufresne
Remove “Command line” sections from docs (#539) by @jdufresne
Use existing key_path() utility function throughout tests (#540) by @jdufresne
Replace force_bytes()/force_unicode() in tests with literals (#541) by @jdufresne
Remove unnecessary Unicode decoding before json.loads() (#542) by @jdufresne
Remove unnecessary force_bytes() calls prior to base64url_decode() (#543) by @jdufresne
Remove deprecated arguments from docs (#544) by @jdufresne
Update code blocks in docs (#545) by @jdufresne
Refactor jwt/jwks_client.py without requests dependency (#546) by @jdufresne
Tighten bytes/str boundaries and remove unnecessary coercing (#547) by @jdufresne
Replace codecs.open() with builtin open() (#548) by @jdufresne
Replace int_from_bytes() with builtin int.from_bytes() (#549) by @jdufresne
Enforce .encode() return type using mypy (#551) by @jdufresne
Prefer direct indexing over options.get() (#552) by @jdufresne
Cleanup “noqa” comments (#553) by @jdufresne
Replace merge_dict() with builtin dict unpacking generalizations (#555) by @jdufresne
Do not mutate the input payload in PyJWT.encode() (#557) by @jdufresne
Use direct indexing in PyJWKClient.get_signing_key_from_jwt() (#558) by @jdufresne
Split PyJWT/PyJWS classes to tighten type interfaces (#559) by @jdufresne
Simplify mocked_response test utility function (#560) by @jdufresne
Autoupdate pre-commit hooks and apply them (#561) by @jdufresne
Remove unused argument “payload” from PyJWS.verifysignature() (#562) by @jdufresne
Add utility functions to assist test skipping (#563) by @jdufresne
Type hint jwt.utils module (#564) by @jdufresne
Prefer ModuleNotFoundError over ImportError (#565) by @jdufresne
Fix tox “manifest” environment to pass (#566) by @jdufresne
Fix tox “docs” environment to pass (#567) by @jdufresne
Simplify black configuration to be closer to upstream defaults (#568) by @jdufresne
Use generator expressions (#569) by @jdufresne
Simplify from_base64url_uint() (#570) by @jdufresne
Drop lint environment from GitHub actions in favor of pre-commit.ci (#571) by @jdufresne
[pre-commit.ci] pre-commit autoupdate (#572)
Simplify tox configuration (#573) by @jdufresne
Combine identical test functions using pytest.mark.parametrize() (#574) by @jdufresne
Complete type hinting of jwks_client.py (#578) by @jdufresne
v1.7.1
Fixed
Update test dependencies with pinned ranges
Fix pytest deprecation warnings
v1.7.0
Changed
Remove CRLF line endings #353
Fixed
Update usage.rst #360
Added
v1.6.4
Fixed
Reverse an unintentional breaking API change to .decode() #352
v1.6.3
Changed
All exceptions inherit from PyJWTError #340
Added
Docs
v1.6.1
Fixed
Audience parameter throws
InvalidAudienceErrorwhen application does not specify an audience, but the token does. #336
v1.6.0
Changed
Fixed
Fix over-eager fallback to stdin #304
Added
Audience parameter now supports iterables #306
v1.5.3
Changed
Increase required version of the cryptography package to >=1.4.0.
Fixed
Remove uses of deprecated functions from the cryptography package.
Warn about missing
algorithmsparam todecode()only whenverifyparam isTrue#281
v1.5.2
Fixed
Ensure correct arguments order in decode super call 7c1e61d
v1.5.1
Changed
Change optparse for argparse. #238
Fixed
Added
v1.5.0
Changed
Add support for ECDSA public keys in RFC 4253 (OpenSSH) format #244
Renamed commandline script
jwttojwt-clito avoid issues with the script clobbering thejwtmodule in some circumstances. #187Better error messages when using an algorithm that requires the cryptography package, but it isn’t available #230
Tokens with future ‘iat’ values are no longer rejected #190
Non-numeric ‘iat’ values now raise InvalidIssuedAtError instead of DecodeError
Remove rejection of future ‘iat’ claims #252
Fixed
Added
Add JWK support for HMAC and RSA keys #202
v1.4.2
Fixed
A PEM-formatted key encoded as bytes could cause a
TypeErrorto be raised #213
v1.4.1
Fixed
v1.4
Fixed
Exclude Python cache files from PyPI releases.
Added
Added new options to require certain claims (require_nbf, require_iat, require_exp) and raise
MissingRequiredClaimErrorif they are not present.If
audience=orissuer=is specified but the claim is not present,MissingRequiredClaimErroris now raised instead ofInvalidAudienceErrorandInvalidIssuerError
v1.3
Fixed
Added
Added a new
jwt.get_unverified_header()to parse and return the header portion of a token prior to signature verification.
Removed
Python 3.2 is no longer a supported platform. This version of Python is rarely used. Users affected by this should upgrade to 3.3+.
v1.2.0
Fixed
Added back
verify_expiration=argument tojwt.decode()that was erroneously removed in v1.1.0.
Changed
Refactored JWS-specific logic out of PyJWT and into PyJWS superclass. #141
Deprecated
verify_expiration=argument tojwt.decode()is now deprecated and will be removed in a future version. Use theoption=argument instead.
v1.1.0
Added
Deprecated
Deprecated usage of the .decode(…, verify=False) parameter.
Fixed
Fixed command line encoding. #128