Skip to content

Commit 9667b78

Browse files
committed
Add option to provide current time when decoding JWT
1 parent be8e914 commit 9667b78

File tree

2 files changed

+43
-8
lines changed

2 files changed

+43
-8
lines changed

jose/jwt.py

+33-8
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,17 @@ def encode(claims, key, algorithm=ALGORITHMS.HS256, headers=None, access_token=N
5353
return jws.sign(claims, key, headers=headers, algorithm=algorithm)
5454

5555

56-
def decode(token, key, algorithms=None, options=None, audience=None, issuer=None, subject=None, access_token=None):
56+
def decode(
57+
token,
58+
key,
59+
algorithms=None,
60+
options=None,
61+
audience=None,
62+
issuer=None,
63+
subject=None,
64+
access_token=None,
65+
now=None,
66+
):
5767
"""Verifies a JWT string's signature and validates reserved claims.
5868
5969
Args:
@@ -74,6 +84,7 @@ def decode(token, key, algorithms=None, options=None, audience=None, issuer=None
7484
claim set, then the access_token must be included, and it must match
7585
the "at_hash" claim.
7686
options (dict): A dictionary of options for skipping validation steps.
87+
now (datetime): Current time. If not set, defaults to current system time.
7788
7889
defaults = {
7990
'verify_signature': True,
@@ -162,6 +173,7 @@ def decode(token, key, algorithms=None, options=None, audience=None, issuer=None
162173
algorithm=algorithm,
163174
access_token=access_token,
164175
options=defaults,
176+
now=now,
165177
)
166178

167179
return claims
@@ -254,7 +266,7 @@ def _validate_iat(claims):
254266
raise JWTClaimsError("Issued At claim (iat) must be an integer.")
255267

256268

257-
def _validate_nbf(claims, leeway=0):
269+
def _validate_nbf(now, claims, leeway=0):
258270
"""Validates that the 'nbf' claim is valid.
259271
260272
The "nbf" (not before) claim identifies the time before which the JWT
@@ -266,6 +278,7 @@ def _validate_nbf(claims, leeway=0):
266278
NumericDate value. Use of this claim is OPTIONAL.
267279
268280
Args:
281+
now (datetime): Current time.
269282
claims (dict): The claims dictionary to validate.
270283
leeway (int): The number of seconds of skew that is allowed.
271284
"""
@@ -278,13 +291,13 @@ def _validate_nbf(claims, leeway=0):
278291
except ValueError:
279292
raise JWTClaimsError("Not Before claim (nbf) must be an integer.")
280293

281-
now = timegm(datetime.utcnow().utctimetuple())
294+
now = timegm(now.utctimetuple())
282295

283296
if nbf > (now + leeway):
284297
raise JWTClaimsError("The token is not yet valid (nbf)")
285298

286299

287-
def _validate_exp(claims, leeway=0):
300+
def _validate_exp(now, claims, leeway=0):
288301
"""Validates that the 'exp' claim is valid.
289302
290303
The "exp" (expiration time) claim identifies the expiration time on
@@ -296,6 +309,7 @@ def _validate_exp(claims, leeway=0):
296309
containing a NumericDate value. Use of this claim is OPTIONAL.
297310
298311
Args:
312+
now (datetime): Current time.
299313
claims (dict): The claims dictionary to validate.
300314
leeway (int): The number of seconds of skew that is allowed.
301315
"""
@@ -308,7 +322,7 @@ def _validate_exp(claims, leeway=0):
308322
except ValueError:
309323
raise JWTClaimsError("Expiration Time claim (exp) must be an integer.")
310324

311-
now = timegm(datetime.utcnow().utctimetuple())
325+
now = timegm(now.utctimetuple())
312326

313327
if exp < (now - leeway):
314328
raise ExpiredSignatureError("Signature has expired.")
@@ -455,7 +469,16 @@ def _validate_at_hash(claims, access_token, algorithm):
455469
raise JWTClaimsError("at_hash claim does not match access_token.")
456470

457471

458-
def _validate_claims(claims, audience=None, issuer=None, subject=None, algorithm=None, access_token=None, options=None):
472+
def _validate_claims(
473+
claims,
474+
audience=None,
475+
issuer=None,
476+
subject=None,
477+
algorithm=None,
478+
access_token=None,
479+
options=None,
480+
now=None,
481+
):
459482

460483
leeway = options.get("leeway", 0)
461484

@@ -475,10 +498,12 @@ def _validate_claims(claims, audience=None, issuer=None, subject=None, algorithm
475498
_validate_iat(claims)
476499

477500
if options.get("verify_nbf"):
478-
_validate_nbf(claims, leeway=leeway)
501+
now = now or datetime.utcnow()
502+
_validate_nbf(now, claims, leeway=leeway)
479503

480504
if options.get("verify_exp"):
481-
_validate_exp(claims, leeway=leeway)
505+
now = now or datetime.utcnow()
506+
_validate_exp(now, claims, leeway=leeway)
482507

483508
if options.get("verify_aud"):
484509
_validate_aud(claims, audience=audience)

tests/test_jwt.py

+10
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,16 @@ def test_exp_skip(self, key):
318318

319319
jwt.decode(token, key, options=options)
320320

321+
def test_time_travel(self, key):
322+
323+
nbf = datetime(2015, 10, 21, 16, 29)
324+
now = datetime(2015, 10, 21, 18, 0)
325+
exp = datetime(2015, 10, 21, 19, 28)
326+
claims = {"exp": exp, "nbf": nbf}
327+
token = jwt.encode(claims, key)
328+
329+
jwt.decode(token, key, now=now)
330+
321331
def test_aud_string(self, key):
322332

323333
aud = "audience"

0 commit comments

Comments
 (0)