Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#444 leeway implementation #445

Merged
merged 2 commits into from
Aug 19, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
implement leeway
  • Loading branch information
domdinicola committed Aug 18, 2021
commit 9b1ae4c6c841baddfc3817af366a17ef605205d1
10 changes: 10 additions & 0 deletions docs/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Some of Simple JWT's behavior can be customized through settings variables in
'AUDIENCE': None,
'ISSUER': None,
'JWK_URL': None,
'LEEWAY': 0,

'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
Expand Down Expand Up @@ -156,6 +157,15 @@ signing of tokens. When using Auth0 for example you might set this to
this field is excluded from the token backend and is not used during
validation.

``LEEWAY``
----------

Leeway is used to give some margin to the expiration time. This can be an
integer for seconds or a ``datetime.timedelta``. Please reference
https://pyjwt.readthedocs.io/en/latest/usage.html#expiration-time-claim-exp
for more information.


``AUTH_HEADER_TYPES``
---------------------

Expand Down
3 changes: 3 additions & 0 deletions rest_framework_simplejwt/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def __init__(
audience=None,
issuer=None,
jwk_url: str = None,
leeway=0,
):
self._validate_algorithm(algorithm)

Expand All @@ -33,6 +34,7 @@ def __init__(
self.issuer = issuer

self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None
self.leeway = leeway

if algorithm.startswith("HS"):
self.verifying_key = signing_key
Expand Down Expand Up @@ -92,6 +94,7 @@ def decode(self, token, verify=True):
verify=verify,
audience=self.audience,
issuer=self.issuer,
leeway=self.leeway,
options={
'verify_aud': self.audience is not None,
'verify_signature': verify,
Expand Down
1 change: 1 addition & 0 deletions rest_framework_simplejwt/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
'AUDIENCE': None,
'ISSUER': None,
'JWK_URL': None,
'LEEWAY': 0,

'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
Expand Down
1 change: 1 addition & 0 deletions rest_framework_simplejwt/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
api_settings.AUDIENCE,
api_settings.ISSUER,
api_settings.JWK_URL,
api_settings.LEEWAY,
)
20 changes: 20 additions & 0 deletions tests/test_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@

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

LEEWAY = 100

class TestTokenBackend(TestCase):
def setUp(self):
self.hmac_token_backend = TokenBackend('HS256', SECRET)
self.hmac_leeway_token_backend = TokenBackend('HS256', SECRET, leeway=LEEWAY)
self.rsa_token_backend = TokenBackend('RS256', PRIVATE_KEY, PUBLIC_KEY)
self.aud_iss_token_backend = TokenBackend('RS256', PRIVATE_KEY, PUBLIC_KEY, AUDIENCE, ISSUER)
self.payload = {'foo': 'bar'}
Expand Down Expand Up @@ -283,3 +285,21 @@ def test_decode_when_token_algorithm_does_not_match(self):

with self.assertRaisesRegex(TokenBackendError, 'Invalid algorithm specified'):
self.hmac_token_backend.decode(token)

def test_decode_leeway_hmac_fail(self):
self.payload["exp"] = datetime_to_epoch(aware_utcnow() - timedelta(seconds=LEEWAY * 2))

expired_token = jwt.encode(self.payload, SECRET, algorithm='HS256')

with self.assertRaises(TokenBackendError):
self.hmac_leeway_token_backend.decode(expired_token)

def test_decode_leeway_hmac_success(self):
self.payload["exp"] = datetime_to_epoch(aware_utcnow() - timedelta(seconds=LEEWAY / 2))

expired_token = jwt.encode(self.payload, SECRET, algorithm='HS256')

self.assertEqual(
self.hmac_leeway_token_backend.decode(expired_token),
self.payload,
)