1
1
"""Basic JSON Web Token implementation."""
2
2
import json
3
3
import logging
4
+ import time
4
5
import uuid
5
- from datetime import datetime
6
- from datetime import timezone
7
6
from json import JSONDecodeError
8
7
9
8
from .exception import HeaderError
@@ -28,9 +27,7 @@ def utc_time_sans_frac():
28
27
29
28
:return: A number of seconds
30
29
"""
31
-
32
- now_timestampt = int (datetime .now (timezone .utc ).timestamp ())
33
- return now_timestampt
30
+ return int (time .time ())
34
31
35
32
36
33
def pick_key (keys , use , alg = "" , key_type = "" , kid = "" ):
@@ -95,6 +92,7 @@ def __init__(
95
92
allowed_sign_algs = None ,
96
93
allowed_enc_algs = None ,
97
94
allowed_enc_encs = None ,
95
+ allowed_max_lifetime = None ,
98
96
zip = "" ,
99
97
):
100
98
self .key_jar = key_jar # KeyJar instance
@@ -115,6 +113,7 @@ def __init__(
115
113
self .allowed_sign_algs = allowed_sign_algs
116
114
self .allowed_enc_algs = allowed_enc_algs
117
115
self .allowed_enc_encs = allowed_enc_encs
116
+ self .allowed_max_lifetime = allowed_max_lifetime
118
117
self .zip = zip
119
118
120
119
def receiver_keys (self , recv , use ):
@@ -176,13 +175,13 @@ def put_together_aud(recv, aud=None):
176
175
177
176
return _aud
178
177
179
- def pack_init (self , recv , aud ):
178
+ def pack_init (self , recv , aud , iat = None ):
180
179
"""
181
180
Gather initial information for the payload.
182
181
183
182
:return: A dictionary with claims and values
184
183
"""
185
- argv = {"iss" : self .iss , "iat" : utc_time_sans_frac ()}
184
+ argv = {"iss" : self .iss , "iat" : iat or utc_time_sans_frac ()}
186
185
if self .lifetime :
187
186
argv ["exp" ] = argv ["iat" ] + self .lifetime
188
187
@@ -207,7 +206,7 @@ def pack_key(self, issuer_id="", kid=""):
207
206
208
207
return keys [0 ] # Might be more then one if kid == ''
209
208
210
- def pack (self , payload = None , kid = "" , issuer_id = "" , recv = "" , aud = None , ** kwargs ):
209
+ def pack (self , payload = None , kid = "" , issuer_id = "" , recv = "" , aud = None , iat = None , ** kwargs ):
211
210
"""
212
211
213
212
:param payload: Information to be carried as payload in the JWT
@@ -216,13 +215,14 @@ def pack(self, payload=None, kid="", issuer_id="", recv="", aud=None, **kwargs):
216
215
:param recv: The intended immediate receiver
217
216
:param aud: Intended audience for this JWS/JWE, not expected to
218
217
contain the recipient.
218
+ :param iat: Override issued at (default current timestamp)
219
219
:param kwargs: Extra keyword arguments
220
220
:return: A signed or signed and encrypted Json Web Token
221
221
"""
222
222
_args = {}
223
223
if payload is not None :
224
224
_args .update (payload )
225
- _args .update (self .pack_init (recv , aud ))
225
+ _args .update (self .pack_init (recv , aud , iat ))
226
226
227
227
try :
228
228
_encrypt = kwargs ["encrypt" ]
@@ -304,11 +304,12 @@ def verify_profile(msg_cls, info, **kwargs):
304
304
raise VerificationError ()
305
305
return _msg
306
306
307
- def unpack (self , token ):
307
+ def unpack (self , token , timestamp = None ):
308
308
"""
309
309
Unpack a received signed or signed and encrypted Json Web Token
310
310
311
311
:param token: The Json Web Token
312
+ :param timestamp: Time for evaluation (default now)
312
313
:return: If decryption and signature verification work the payload
313
314
will be returned as a Message instance if possible.
314
315
"""
@@ -378,6 +379,26 @@ def unpack(self, token):
378
379
except KeyError :
379
380
_msg_cls = None
380
381
382
+ timestamp = timestamp or utc_time_sans_frac ()
383
+
384
+ if "nbf" in _info :
385
+ nbf = int (_info ["nbf" ])
386
+ if timestamp < nbf - self .skew :
387
+ raise VerificationError ("Token not yet valid" )
388
+
389
+ if "exp" in _info :
390
+ exp = int (_info ["exp" ])
391
+ if timestamp >= exp + self .skew :
392
+ raise VerificationError ("Token expired" )
393
+ else :
394
+ exp = None
395
+
396
+ if "iat" in _info :
397
+ iat = int (_info ["iat" ])
398
+ if self .allowed_max_lifetime and exp :
399
+ if abs (exp - iat ) > self .allowed_max_lifetime :
400
+ raise VerificationError ("Token lifetime exceeded" )
401
+
381
402
if _msg_cls :
382
403
vp_args = {"skew" : self .skew }
383
404
if self .iss :
0 commit comments