feat(jwt): add leeway support for token expiration verification#4643
Open
Krishnachaitanyakc wants to merge 11 commits intolitestar-org:mainfrom
Open
feat(jwt): add leeway support for token expiration verification#4643Krishnachaitanyakc wants to merge 11 commits intolitestar-org:mainfrom
Krishnachaitanyakc wants to merge 11 commits intolitestar-org:mainfrom
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4643 +/- ##
==========================================
+ Coverage 67.32% 67.36% +0.03%
==========================================
Files 292 292
Lines 14925 14941 +16
Branches 1673 1673
==========================================
+ Hits 10049 10065 +16
Misses 4739 4739
Partials 137 137 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
sobolevn
reviewed
Mar 25, 2026
Member
sobolevn
left a comment
There was a problem hiding this comment.
I don't think that this implementation is correct, please take a look at https://github.com/wemake-services/django-modern-rest/blob/8033c9ed0d871ff413875350a4dc664bd6f174ba/dmr/security/jwt/token.py#L75
3b275d2 to
9867863
Compare
Author
|
@sobolevn Thanks for the pointer to the What changed:
|
JacobCoffee
requested changes
Mar 26, 2026
Member
JacobCoffee
left a comment
There was a problem hiding this comment.
need to get CI all passing as well :)
Add a `leeway` parameter to JWT auth configuration, middleware, and token decoding to allow a time margin (in seconds or as a timedelta) for clock skew when verifying `exp` and `nbf` claims. This delegates to PyJWT's built-in leeway support. Closes litestar-org#4584
- Extract _build_decode_options and _construct_without_validation from Token.decode to reduce cyclomatic complexity below the C901 threshold - Add missing leeway parameter to CustomToken.decode_payload override in tests to match the updated base class signature
Replace :func:`jwt.decode` with ``jwt.decode`` to avoid Sphinx cross-reference warning since PyJWT is not in the intersphinx mapping.
Covers the except TypeError fallback when a subclass overrides decode_payload without the leeway parameter, improving patch coverage.
…test Add type: ignore[override] and pyright: ignore[reportIncompatibleMethodOverride] to the LegacyToken.decode_payload test override, which intentionally omits the leeway parameter to test backward compatibility.
…dependency Replace msgspec.convert() with direct cls(**payload, leeway=...) construction now that leeway is an InitVar handled in __post_init__. This eliminates: - The _construct_without_validation bypass method - The TypeError catch for backward-compat decode_payload calls - The msgspec import and ValidationError in the except clause The leeway InitVar approach (per sobolevn's suggestion) lets __post_init__ apply the leeway tolerance to exp validation directly, so there is no need to skip validation for recently-expired tokens.
…class signature The Token dataclass has an InitVar[float] leeway field, which means __post_init__ receives it as a parameter. The CustomToken subclass in the test was overriding __post_init__ without this parameter, causing a mypy [override] error.
- Remove default value from CustomToken.__post_init__ leeway param (defaults should be on InitVar field, not __post_init__ parameter) - Pass random_field as int since msgspec type coercion is no longer used
6226024 to
841ccf4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds support for the
leewayparameter in JWT token decoding, as requested in #4584.leewayfield (typefloat | timedelta, default0) toBaseJWTAuth,JWTAuth,JWTCookieAuth, andOAuth2PasswordBearerAuthleewaythroughJWTAuthenticationMiddlewareandJWTCookieAuthenticationMiddlewaretoToken.decodeandToken.decode_payloadToken.decode_payloadpassesleewaydirectly to PyJWT'sjwt.decode(), which natively supports itdecode_payloadwithout theleewayparameter, the call falls back to the old signature via aTypeErrorcatchleewayis set,Token.decodebypasses__post_init__validation (which rejects expiredexpvalues) since PyJWT already validated expiry with leeway appliedCloses #4584
Test plan
test_decode_with_leeway_allows_recently_expired_token-- verifies a token expired by 5 seconds is accepted with 10 seconds of leeway (parametrized overint,float, andtimedelta)test_decode_with_leeway_does_not_allow_long_expired_token-- verifies a token expired by 1 hour is still rejected with 10 seconds of leewaytest_custom_decode_payloadfor backward compatibility)📚 Documentation preview 📚: https://litestar-org.github.io/litestar-docs-preview/4643