Skip to content

Commit 4d7c764

Browse files
#444 leeway implementation (#445)
* implement leeway * Update CHANGELOG.md for 4.8.0 Co-authored-by: Andrew Chen Wang <[email protected]>
1 parent e74f4eb commit 4d7c764

File tree

6 files changed

+40
-1
lines changed

6 files changed

+40
-1
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
## Unreleased
22

3-
## Version 4.7.3
3+
## Version 4.8.0
44

55
* Add integration instructions for drf-yasg ([#145](https://github.com/jazzband/djangorestframework-simplejwt/pull/145))
66
* Verify Serializer Should Honour Blacklist ([#239](https://github.com/jazzband/djangorestframework-simplejwt/pull/239))
77
* Added missing import in getting_started docs ([#431](https://github.com/jazzband/djangorestframework-simplejwt/pull/431))
88
* Use import_string for token_backend ([#435](https://github.com/jazzband/djangorestframework-simplejwt/pull/435))
9+
* Add JWKS support ([#437](https://github.com/jazzband/djangorestframework-simplejwt/pull/435))
10+
* Use pathlib instead of open in setup.py ([#339](https://github.com/jazzband/djangorestframework-simplejwt/pull/339))
11+
* Optimize default_user_authentication_rule ([#441](https://github.com/jazzband/djangorestframework-simplejwt/pull/441))
12+
* Add Leeway option to decode ([#445](https://github.com/jazzband/djangorestframework-simplejwt/pull/445))
913

1014
## Version 4.7.2
1115

docs/settings.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Some of Simple JWT's behavior can be customized through settings variables in
2727
'AUDIENCE': None,
2828
'ISSUER': None,
2929
'JWK_URL': None,
30+
'LEEWAY': 0,
3031
3132
'AUTH_HEADER_TYPES': ('Bearer',),
3233
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
@@ -156,6 +157,15 @@ signing of tokens. When using Auth0 for example you might set this to
156157
this field is excluded from the token backend and is not used during
157158
validation.
158159

160+
``LEEWAY``
161+
----------
162+
163+
Leeway is used to give some margin to the expiration time. This can be an
164+
integer for seconds or a ``datetime.timedelta``. Please reference
165+
https://pyjwt.readthedocs.io/en/latest/usage.html#expiration-time-claim-exp
166+
for more information.
167+
168+
159169
``AUTH_HEADER_TYPES``
160170
---------------------
161171

rest_framework_simplejwt/backends.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def __init__(
2424
audience=None,
2525
issuer=None,
2626
jwk_url: str = None,
27+
leeway=0,
2728
):
2829
self._validate_algorithm(algorithm)
2930

@@ -33,6 +34,7 @@ def __init__(
3334
self.issuer = issuer
3435

3536
self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None
37+
self.leeway = leeway
3638

3739
if algorithm.startswith("HS"):
3840
self.verifying_key = signing_key
@@ -92,6 +94,7 @@ def decode(self, token, verify=True):
9294
verify=verify,
9395
audience=self.audience,
9496
issuer=self.issuer,
97+
leeway=self.leeway,
9598
options={
9699
'verify_aud': self.audience is not None,
97100
'verify_signature': verify,

rest_framework_simplejwt/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'AUDIENCE': None,
2323
'ISSUER': None,
2424
'JWK_URL': None,
25+
'LEEWAY': 0,
2526

2627
'AUTH_HEADER_TYPES': ('Bearer',),
2728
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',

rest_framework_simplejwt/state.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88
api_settings.AUDIENCE,
99
api_settings.ISSUER,
1010
api_settings.JWK_URL,
11+
api_settings.LEEWAY,
1112
)

tests/test_backends.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121

2222
JWK_URL = 'https://randomstring.auth0.com/.well-known/jwks.json'
2323

24+
LEEWAY = 100
2425

2526
class TestTokenBackend(TestCase):
2627
def setUp(self):
2728
self.hmac_token_backend = TokenBackend('HS256', SECRET)
29+
self.hmac_leeway_token_backend = TokenBackend('HS256', SECRET, leeway=LEEWAY)
2830
self.rsa_token_backend = TokenBackend('RS256', PRIVATE_KEY, PUBLIC_KEY)
2931
self.aud_iss_token_backend = TokenBackend('RS256', PRIVATE_KEY, PUBLIC_KEY, AUDIENCE, ISSUER)
3032
self.payload = {'foo': 'bar'}
@@ -283,3 +285,21 @@ def test_decode_when_token_algorithm_does_not_match(self):
283285

284286
with self.assertRaisesRegex(TokenBackendError, 'Invalid algorithm specified'):
285287
self.hmac_token_backend.decode(token)
288+
289+
def test_decode_leeway_hmac_fail(self):
290+
self.payload["exp"] = datetime_to_epoch(aware_utcnow() - timedelta(seconds=LEEWAY * 2))
291+
292+
expired_token = jwt.encode(self.payload, SECRET, algorithm='HS256')
293+
294+
with self.assertRaises(TokenBackendError):
295+
self.hmac_leeway_token_backend.decode(expired_token)
296+
297+
def test_decode_leeway_hmac_success(self):
298+
self.payload["exp"] = datetime_to_epoch(aware_utcnow() - timedelta(seconds=LEEWAY / 2))
299+
300+
expired_token = jwt.encode(self.payload, SECRET, algorithm='HS256')
301+
302+
self.assertEqual(
303+
self.hmac_leeway_token_backend.decode(expired_token),
304+
self.payload,
305+
)

0 commit comments

Comments
 (0)