Skip to content

Commit 8fd0ed9

Browse files
committed
Add option to provide current time when decoding JWT
1 parent 018b310 commit 8fd0ed9

File tree

2 files changed

+44
-8
lines changed

2 files changed

+44
-8
lines changed

jose/jwt.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,17 @@ def encode(claims, key, algorithm=ALGORITHMS.HS256, headers=None, access_token=N
6363
return jws.sign(claims, key, headers=headers, algorithm=algorithm)
6464

6565

66-
def decode(token, key, algorithms=None, options=None, audience=None, issuer=None, subject=None, access_token=None):
66+
def decode(
67+
token,
68+
key,
69+
algorithms=None,
70+
options=None,
71+
audience=None,
72+
issuer=None,
73+
subject=None,
74+
access_token=None,
75+
now=None,
76+
):
6777
"""Verifies a JWT string's signature and validates reserved claims.
6878
6979
Args:
@@ -91,6 +101,7 @@ def decode(token, key, algorithms=None, options=None, audience=None, issuer=None
91101
claim set, then the access_token must be included, and it must match
92102
the "at_hash" claim.
93103
options (dict): A dictionary of options for skipping validation steps.
104+
now (datetime): Current time. If not set, defaults to current system time.
94105
95106
defaults = {
96107
'verify_signature': True,
@@ -179,6 +190,7 @@ def decode(token, key, algorithms=None, options=None, audience=None, issuer=None
179190
algorithm=algorithm,
180191
access_token=access_token,
181192
options=defaults,
193+
now=now,
182194
)
183195

184196
return claims
@@ -271,7 +283,7 @@ def _validate_iat(claims):
271283
raise JWTClaimsError("Issued At claim (iat) must be an integer.")
272284

273285

274-
def _validate_nbf(claims, leeway=0):
286+
def _validate_nbf(now, claims, leeway=0):
275287
"""Validates that the 'nbf' claim is valid.
276288
277289
The "nbf" (not before) claim identifies the time before which the JWT
@@ -283,6 +295,7 @@ def _validate_nbf(claims, leeway=0):
283295
NumericDate value. Use of this claim is OPTIONAL.
284296
285297
Args:
298+
now (datetime): Current time.
286299
claims (dict): The claims dictionary to validate.
287300
leeway (int): The number of seconds of skew that is allowed.
288301
"""
@@ -295,13 +308,13 @@ def _validate_nbf(claims, leeway=0):
295308
except ValueError:
296309
raise JWTClaimsError("Not Before claim (nbf) must be an integer.")
297310

298-
now = timegm(datetime.now(UTC).utctimetuple())
311+
now = timegm(now.utctimetuple())
299312

300313
if nbf > (now + leeway):
301314
raise JWTClaimsError("The token is not yet valid (nbf)")
302315

303316

304-
def _validate_exp(claims, leeway=0):
317+
def _validate_exp(now, claims, leeway=0):
305318
"""Validates that the 'exp' claim is valid.
306319
307320
The "exp" (expiration time) claim identifies the expiration time on
@@ -313,6 +326,7 @@ def _validate_exp(claims, leeway=0):
313326
containing a NumericDate value. Use of this claim is OPTIONAL.
314327
315328
Args:
329+
now (datetime): Current time.
316330
claims (dict): The claims dictionary to validate.
317331
leeway (int): The number of seconds of skew that is allowed.
318332
"""
@@ -325,7 +339,7 @@ def _validate_exp(claims, leeway=0):
325339
except ValueError:
326340
raise JWTClaimsError("Expiration Time claim (exp) must be an integer.")
327341

328-
now = timegm(datetime.now(UTC).utctimetuple())
342+
now = timegm(now.utctimetuple())
329343

330344
if exp < (now - leeway):
331345
raise ExpiredSignatureError("Signature has expired.")
@@ -472,7 +486,17 @@ def _validate_at_hash(claims, access_token, algorithm):
472486
raise JWTClaimsError("at_hash claim does not match access_token.")
473487

474488

475-
def _validate_claims(claims, audience=None, issuer=None, subject=None, algorithm=None, access_token=None, options=None):
489+
def _validate_claims(
490+
claims,
491+
audience=None,
492+
issuer=None,
493+
subject=None,
494+
algorithm=None,
495+
access_token=None,
496+
options=None,
497+
now=None,
498+
):
499+
476500
leeway = options.get("leeway", 0)
477501

478502
if isinstance(leeway, timedelta):
@@ -491,10 +515,12 @@ def _validate_claims(claims, audience=None, issuer=None, subject=None, algorithm
491515
_validate_iat(claims)
492516

493517
if options.get("verify_nbf"):
494-
_validate_nbf(claims, leeway=leeway)
518+
now = now or datetime.now(UTC)
519+
_validate_nbf(now, claims, leeway=leeway)
495520

496521
if options.get("verify_exp"):
497-
_validate_exp(claims, leeway=leeway)
522+
now = now or datetime.now(UTC)
523+
_validate_exp(now, claims, leeway=leeway)
498524

499525
if options.get("verify_aud"):
500526
_validate_aud(claims, audience=audience)

tests/test_jwt.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,16 @@ def test_exp_skip(self, key):
311311

312312
jwt.decode(token, key, options=options)
313313

314+
def test_time_travel(self, key):
315+
316+
nbf = datetime(2015, 10, 21, 16, 29)
317+
now = datetime(2015, 10, 21, 18, 0)
318+
exp = datetime(2015, 10, 21, 19, 28)
319+
claims = {"exp": exp, "nbf": nbf}
320+
token = jwt.encode(claims, key)
321+
322+
jwt.decode(token, key, now=now)
323+
314324
def test_aud_string(self, key):
315325
aud = "audience"
316326

0 commit comments

Comments
 (0)