Skip to content

Commit 7a66496

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

File tree

2 files changed

+22
-8
lines changed

2 files changed

+22
-8
lines changed

jose/jwt.py

+12-8
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ 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(token, key, algorithms=None, options=None, audience=None, issuer=None, subject=None, access_token=None, now=None):
5757
"""Verifies a JWT string's signature and validates reserved claims.
5858
5959
Args:
@@ -73,6 +73,7 @@ def decode(token, key, algorithms=None, options=None, audience=None, issuer=None
7373
access_token (str): An access token string. If the "at_hash" claim is included in the
7474
claim set, then the access_token must be included, and it must match
7575
the "at_hash" claim.
76+
now (datetime): Current time. If not set, defaults to current system time.
7677
options (dict): A dictionary of options for skipping validation steps.
7778
7879
defaults = {
@@ -155,6 +156,7 @@ def decode(token, key, algorithms=None, options=None, audience=None, issuer=None
155156
raise JWTError("Invalid payload string: must be a json object")
156157

157158
_validate_claims(
159+
now or datetime.utcnow(),
158160
claims,
159161
audience=audience,
160162
issuer=issuer,
@@ -254,7 +256,7 @@ def _validate_iat(claims):
254256
raise JWTClaimsError("Issued At claim (iat) must be an integer.")
255257

256258

257-
def _validate_nbf(claims, leeway=0):
259+
def _validate_nbf(now, claims, leeway=0):
258260
"""Validates that the 'nbf' claim is valid.
259261
260262
The "nbf" (not before) claim identifies the time before which the JWT
@@ -266,6 +268,7 @@ def _validate_nbf(claims, leeway=0):
266268
NumericDate value. Use of this claim is OPTIONAL.
267269
268270
Args:
271+
now (datetime): Current time.
269272
claims (dict): The claims dictionary to validate.
270273
leeway (int): The number of seconds of skew that is allowed.
271274
"""
@@ -278,13 +281,13 @@ def _validate_nbf(claims, leeway=0):
278281
except ValueError:
279282
raise JWTClaimsError("Not Before claim (nbf) must be an integer.")
280283

281-
now = timegm(datetime.utcnow().utctimetuple())
284+
now = timegm(now.utctimetuple())
282285

283286
if nbf > (now + leeway):
284287
raise JWTClaimsError("The token is not yet valid (nbf)")
285288

286289

287-
def _validate_exp(claims, leeway=0):
290+
def _validate_exp(now, claims, leeway=0):
288291
"""Validates that the 'exp' claim is valid.
289292
290293
The "exp" (expiration time) claim identifies the expiration time on
@@ -296,6 +299,7 @@ def _validate_exp(claims, leeway=0):
296299
containing a NumericDate value. Use of this claim is OPTIONAL.
297300
298301
Args:
302+
now (datetime): Current time.
299303
claims (dict): The claims dictionary to validate.
300304
leeway (int): The number of seconds of skew that is allowed.
301305
"""
@@ -308,7 +312,7 @@ def _validate_exp(claims, leeway=0):
308312
except ValueError:
309313
raise JWTClaimsError("Expiration Time claim (exp) must be an integer.")
310314

311-
now = timegm(datetime.utcnow().utctimetuple())
315+
now = timegm(now.utctimetuple())
312316

313317
if exp < (now - leeway):
314318
raise ExpiredSignatureError("Signature has expired.")
@@ -455,7 +459,7 @@ def _validate_at_hash(claims, access_token, algorithm):
455459
raise JWTClaimsError("at_hash claim does not match access_token.")
456460

457461

458-
def _validate_claims(claims, audience=None, issuer=None, subject=None, algorithm=None, access_token=None, options=None):
462+
def _validate_claims(now, claims, audience=None, issuer=None, subject=None, algorithm=None, access_token=None, options=None):
459463

460464
leeway = options.get("leeway", 0)
461465

@@ -475,10 +479,10 @@ def _validate_claims(claims, audience=None, issuer=None, subject=None, algorithm
475479
_validate_iat(claims)
476480

477481
if options.get("verify_nbf"):
478-
_validate_nbf(claims, leeway=leeway)
482+
_validate_nbf(now, claims, leeway=leeway)
479483

480484
if options.get("verify_exp"):
481-
_validate_exp(claims, leeway=leeway)
485+
_validate_exp(now, claims, leeway=leeway)
482486

483487
if options.get("verify_aud"):
484488
_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)