diff --git a/README.md b/README.md index f64b645c..3a2882c8 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,13 @@ time.sleep(32) jwt.decode(jwt_payload, 'secret', leeway=10) ``` +Instead of specifying the leeway as a number of seconds, a `datetime.timedelta` instance can be used. The last line in the example above is equivalent to: + +```python +jwt.decode(jwt_payload, 'secret', leeway=datetime.timedelta(seconds=10)) +``` + + ### Not Before Time Claim > The nbf (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. The processing of the nbf claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the nbf claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL. diff --git a/jwt/__init__.py b/jwt/__init__.py index ad581885..b8244dc7 100644 --- a/jwt/__init__.py +++ b/jwt/__init__.py @@ -11,7 +11,7 @@ import hmac import sys -from datetime import datetime +from datetime import datetime, timedelta from calendar import timegm from collections import Mapping @@ -359,6 +359,17 @@ def load(jwt): def verify_signature(payload, signing_input, header, signature, key='', verify_expiration=True, leeway=0, **kwargs): + + if isinstance(leeway, timedelta): + try: + leeway.total_seconds + except AttributeError: + # On Python 2.6, timedelta instances do not have + # a .total_seconds() method. + leeway = leeway.days * 24 * 60 * 60 + leeway.seconds + else: + leeway = leeway.total_seconds() + try: algorithm = header['alg'].upper() key = prepare_key_methods[algorithm](key) diff --git a/tests/test_jwt.py b/tests/test_jwt.py index 04146a24..76ae8383 100644 --- a/tests/test_jwt.py +++ b/tests/test_jwt.py @@ -5,7 +5,7 @@ import time from calendar import timegm -from datetime import datetime +from datetime import datetime, timedelta from decimal import Decimal import jwt @@ -411,20 +411,23 @@ def test_decode_with_expiration_with_leeway(self): decoded_payload, signing, header, signature = jwt.load(jwt_message) # With 3 seconds leeway, should be ok - jwt.decode(jwt_message, secret, leeway=3) + for leeway in (3, timedelta(seconds=3)): + jwt.decode(jwt_message, secret, leeway=leeway) - jwt.verify_signature(decoded_payload, signing, header, - signature, secret, leeway=3) + jwt.verify_signature(decoded_payload, signing, header, + signature, secret, leeway=leeway) # With 1 seconds, should fail - self.assertRaises( - jwt.ExpiredSignature, - lambda: jwt.decode(jwt_message, secret, leeway=1)) - - self.assertRaises( - jwt.ExpiredSignature, - lambda: jwt.verify_signature(decoded_payload, signing, - header, signature, secret, leeway=1)) + for leeway in (1, timedelta(seconds=1)): + self.assertRaises( + jwt.ExpiredSignature, + lambda: jwt.decode(jwt_message, secret, leeway=leeway)) + + self.assertRaises( + jwt.ExpiredSignature, + lambda: jwt.verify_signature(decoded_payload, signing, + header, signature, secret, + leeway=leeway)) def test_decode_with_notbefore_with_leeway(self): self.payload['nbf'] = utc_timestamp() + 10