@@ -42,7 +42,7 @@ def decode_id_token(id_token, client_id=None, issuer=None, nonce=None, now=None)
42
42
"""
43
43
decoded = json .loads (decode_part (id_token .split ('.' )[1 ]))
44
44
err = None # https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
45
- _now = now or time .time ()
45
+ _now = int ( now or time .time () )
46
46
skew = 120 # 2 minutes
47
47
if _now + skew < decoded .get ("nbf" , _now - 1 ): # nbf is optional per JWT specs
48
48
# This is not an ID token validation, but a JWT validation
@@ -67,14 +67,14 @@ def decode_id_token(id_token, client_id=None, issuer=None, nonce=None, now=None)
67
67
# the Client and the Token Endpoint (which it is during _obtain_token()),
68
68
# the TLS server validation MAY be used to validate the issuer
69
69
# in place of checking the token signature.
70
- if _now > decoded ["exp" ]:
70
+ if _now - skew > decoded ["exp" ]:
71
71
err = "9. The current time MUST be before the time represented by the exp Claim."
72
72
if nonce and nonce != decoded .get ("nonce" ):
73
73
err = ("11. Nonce must be the same value "
74
74
"as the one that was sent in the Authentication Request." )
75
75
if err :
76
- raise RuntimeError ("%s The id_token was: %s" % (
77
- err , json .dumps (decoded , indent = 2 )))
76
+ raise RuntimeError ("%s Current epoch = %s. The id_token was: %s" % (
77
+ err , _now , json .dumps (decoded , indent = 2 )))
78
78
return decoded
79
79
80
80
@@ -187,6 +187,8 @@ def initiate_auth_code_flow(
187
187
flow = super (Client , self ).initiate_auth_code_flow (
188
188
scope = _scope , nonce = _nonce_hash (nonce ), ** kwargs )
189
189
flow ["nonce" ] = nonce
190
+ if kwargs .get ("max_age" ) is not None :
191
+ flow ["max_age" ] = kwargs ["max_age" ]
190
192
return flow
191
193
192
194
def obtain_token_by_auth_code_flow (self , auth_code_flow , auth_response , ** kwargs ):
@@ -208,6 +210,26 @@ def obtain_token_by_auth_code_flow(self, auth_code_flow, auth_response, **kwargs
208
210
raise RuntimeError (
209
211
'The nonce in id token ("%s") should match our nonce ("%s")' %
210
212
(nonce_in_id_token , expected_hash ))
213
+
214
+ if auth_code_flow .get ("max_age" ) is not None :
215
+ auth_time = result .get ("id_token_claims" , {}).get ("auth_time" )
216
+ if not auth_time :
217
+ raise RuntimeError (
218
+ "13. max_age was requested, ID token should contain auth_time" )
219
+ now = int (time .time ())
220
+ skew = 120 # 2 minutes. Hardcoded, for now
221
+ if now - skew > auth_time + auth_code_flow ["max_age" ]:
222
+ raise RuntimeError (
223
+ "13. auth_time ({auth_time}) was requested, "
224
+ "by using max_age ({max_age}) parameter, "
225
+ "and now ({now}) too much time has elasped "
226
+ "since last end-user authentication. "
227
+ "The ID token was: {id_token}" .format (
228
+ auth_time = auth_time ,
229
+ max_age = auth_code_flow ["max_age" ],
230
+ now = now ,
231
+ id_token = json .dumps (result ["id_token_claims" ], indent = 2 ),
232
+ ))
211
233
return result
212
234
213
235
def obtain_token_by_browser (
0 commit comments