diff options
author | Mark Adams <mark@markadams.me> | 2015-03-29 15:36:18 -0500 |
---|---|---|
committer | Mark Adams <mark@markadams.me> | 2015-03-29 15:36:18 -0500 |
commit | 83d07fc98743a47ee411ce48805ddaf053672925 (patch) | |
tree | c2c7fe6a460cd88dec807d79a32d0a2e17cd826a | |
parent | 54ed26b6e3dbad1e27679c9e91fa30dfa7a98736 (diff) | |
download | pyjwt-83d07fc98743a47ee411ce48805ddaf053672925.tar.gz |
Added checks on iat to make sure that a token can't be issued for the
future
Changed nbf exception to ImmatureSignatureError
-rw-r--r-- | jwt/__init__.py | 6 | ||||
-rw-r--r-- | jwt/api.py | 20 | ||||
-rw-r--r-- | jwt/exceptions.py | 10 | ||||
-rw-r--r-- | tests/test_api.py | 18 |
4 files changed, 36 insertions, 18 deletions
diff --git a/jwt/__init__.py b/jwt/__init__.py index 16b4f6d..4d87657 100644 --- a/jwt/__init__.py +++ b/jwt/__init__.py @@ -20,7 +20,7 @@ from .api import ( encode, decode, register_algorithm, unregister_algorithm, PyJWT ) from .exceptions import ( - InvalidTokenError, DecodeError, ExpiredSignatureError, - InvalidAudienceError, InvalidIssuerError, - ExpiredSignature, InvalidAudience, InvalidIssuer + InvalidTokenError, DecodeError, InvalidAudienceError, + ExpiredSignatureError, ImmatureSignatureError, InvalidIssuedAtError, + InvalidIssuerError, ExpiredSignature, InvalidAudience, InvalidIssuer ) @@ -9,7 +9,8 @@ from .algorithms import Algorithm, get_default_algorithms # NOQA from .compat import string_types, text_type, timedelta_total_seconds from .exceptions import ( DecodeError, ExpiredSignatureError, InvalidAlgorithmError, - InvalidAudienceError, InvalidIssuerError + InvalidAudienceError, InvalidIssuerError, + InvalidIssuedAtError, ImmatureSignatureError ) from .utils import base64url_decode, base64url_encode @@ -184,22 +185,25 @@ class PyJWT(object): if not isinstance(audience, (string_types, type(None))): raise TypeError('audience must be a string or None') + now = timegm(datetime.utcnow().utctimetuple()) + if 'iat' in payload: try: - int(payload['iat']) + iat = int(payload['iat']) except ValueError: raise DecodeError('Issued At claim (iat) must be an integer.') + if iat > (now + leeway): + raise InvalidIssuedAtError('Issued At claim (iat) cannot be in the future.') + if 'nbf' in payload and verify_expiration: try: nbf = int(payload['nbf']) except ValueError: raise DecodeError('Not Before claim (nbf) must be an integer.') - utc_timestamp = timegm(datetime.utcnow().utctimetuple()) - - if nbf > (utc_timestamp + leeway): - raise ExpiredSignatureError('Signature not yet valid') + if nbf > (now + leeway): + raise ImmatureSignatureError('The token is not yet valid (nbf)') if 'exp' in payload and verify_expiration: try: @@ -207,9 +211,7 @@ class PyJWT(object): except ValueError: raise DecodeError('Expiration Time claim (exp) must be an integer.') - utc_timestamp = timegm(datetime.utcnow().utctimetuple()) - - if exp < (utc_timestamp - leeway): + if exp < (now - leeway): raise ExpiredSignatureError('Signature has expired') if 'aud' in payload: diff --git a/jwt/exceptions.py b/jwt/exceptions.py index 6fb96eb..67d8819 100644 --- a/jwt/exceptions.py +++ b/jwt/exceptions.py @@ -18,10 +18,18 @@ class InvalidIssuerError(InvalidTokenError): pass -class InvalidKeyError(Exception): +class InvalidIssuedAtError(InvalidTokenError): + pass + + +class ImmatureSignatureError(InvalidTokenError): pass +class InvalidKeyError(Exception): + pass + + class InvalidAlgorithmError(InvalidTokenError): pass diff --git a/tests/test_api.py b/tests/test_api.py index 704f1be..ee90762 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -10,7 +10,8 @@ from jwt.algorithms import Algorithm from jwt.api import PyJWT from jwt.exceptions import ( DecodeError, ExpiredSignatureError, InvalidAlgorithmError, - InvalidAudienceError, InvalidIssuerError + InvalidAudienceError, InvalidIssuerError, InvalidIssuedAtError, + ImmatureSignatureError ) from .compat import text_type, unittest @@ -210,7 +211,7 @@ class TestAPI(unittest.TestCase): 'eyJpYXQiOiJub3QtYW4taW50In0.' 'H1GmcQgSySa5LOKYbzGm--b1OmRbHFkyk8pq811FzZM') - with self.assertRaisesRegexp(DecodeError, 'iat'): + with self.assertRaises(DecodeError): self.jwt.decode(example_jwt, 'secret') def test_decode_raises_exception_if_nbf_is_not_int(self): @@ -219,9 +220,16 @@ class TestAPI(unittest.TestCase): 'eyJuYmYiOiJub3QtYW4taW50In0.' 'c25hldC8G2ZamC8uKpax9sYMTgdZo3cxrmzFHaAAluw') - with self.assertRaisesRegexp(DecodeError, 'nbf'): + with self.assertRaises(DecodeError): self.jwt.decode(example_jwt, 'secret') + def test_decode_raises_exception_if_iat_in_the_future(self): + now = datetime.utcnow() + token = self.jwt.encode({'iat': now + timedelta(days=1)}, key='secret') + + with self.assertRaises(InvalidIssuedAtError): + self.jwt.decode(token, 'secret') + def test_encode_datetime(self): secret = 'secret' current_datetime = datetime.utcnow() @@ -451,7 +459,7 @@ class TestAPI(unittest.TestCase): secret = 'secret' jwt_message = self.jwt.encode(self.payload, secret) - with self.assertRaises(ExpiredSignatureError): + with self.assertRaises(ImmatureSignatureError): self.jwt.decode(jwt_message, secret) def test_decode_skip_expiration_verification(self): @@ -492,7 +500,7 @@ class TestAPI(unittest.TestCase): # With 13 seconds leeway, should be ok self.jwt.decode(jwt_message, secret, leeway=13) - with self.assertRaises(ExpiredSignatureError): + with self.assertRaises(ImmatureSignatureError): self.jwt.decode(jwt_message, secret, leeway=1) def test_decode_with_algo_none_should_fail(self): |