summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Adams <mark@markadams.me>2015-03-29 15:36:18 -0500
committerMark Adams <mark@markadams.me>2015-03-29 15:36:18 -0500
commit83d07fc98743a47ee411ce48805ddaf053672925 (patch)
treec2c7fe6a460cd88dec807d79a32d0a2e17cd826a
parent54ed26b6e3dbad1e27679c9e91fa30dfa7a98736 (diff)
downloadpyjwt-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__.py6
-rw-r--r--jwt/api.py20
-rw-r--r--jwt/exceptions.py10
-rw-r--r--tests/test_api.py18
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
)
diff --git a/jwt/api.py b/jwt/api.py
index 03585b3..52ff14e 100644
--- a/jwt/api.py
+++ b/jwt/api.py
@@ -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):