summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Adams <mark@markadams.me>2015-03-29 18:03:07 -0500
committerMark Adams <mark@markadams.me>2015-03-29 18:03:07 -0500
commita2601ad46433a99c8777a74abeaf4dfd70630d17 (patch)
tree0e5878da3910f593d1a9999fa33cad1acf414350
parent54ed26b6e3dbad1e27679c9e91fa30dfa7a98736 (diff)
parent49db1ad0c97f4d90554af52b175abf03e255f572 (diff)
downloadpyjwt-a2601ad46433a99c8777a74abeaf4dfd70630d17.tar.gz
Merge pull request #126 from mark-adams/iat-nbf-checks
Add check for iat and new exception for nbf claims
-rw-r--r--jwt/__init__.py6
-rw-r--r--jwt/api.py22
-rw-r--r--jwt/exceptions.py8
-rw-r--r--tests/test_api.py20
4 files changed, 37 insertions, 19 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..6d39d8d 100644
--- a/jwt/api.py
+++ b/jwt/api.py
@@ -8,8 +8,9 @@ from datetime import datetime, timedelta
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
+ DecodeError, ExpiredSignatureError, ImmatureSignatureError,
+ InvalidAlgorithmError, InvalidAudienceError, InvalidIssuedAtError,
+ InvalidIssuerError
)
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..0e82e6f 100644
--- a/jwt/exceptions.py
+++ b/jwt/exceptions.py
@@ -18,6 +18,14 @@ class InvalidIssuerError(InvalidTokenError):
pass
+class InvalidIssuedAtError(InvalidTokenError):
+ pass
+
+
+class ImmatureSignatureError(InvalidTokenError):
+ pass
+
+
class InvalidKeyError(Exception):
pass
diff --git a/tests/test_api.py b/tests/test_api.py
index 704f1be..f1734b4 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -9,8 +9,9 @@ from decimal import Decimal
from jwt.algorithms import Algorithm
from jwt.api import PyJWT
from jwt.exceptions import (
- DecodeError, ExpiredSignatureError, InvalidAlgorithmError,
- InvalidAudienceError, InvalidIssuerError
+ DecodeError, ExpiredSignatureError, ImmatureSignatureError,
+ InvalidAlgorithmError, InvalidAudienceError, InvalidIssuedAtError,
+ InvalidIssuerError
)
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):