@@ -226,6 +226,9 @@ def __init__(self, hs: "HomeServer"):
226
226
burst_count = self .hs .config .rc_login_failed_attempts .burst_count ,
227
227
)
228
228
229
+ # The number of seconds to keep a UI auth session active.
230
+ self ._ui_auth_session_timeout = hs .config .ui_auth_session_timeout
231
+
229
232
# Ratelimitier for failed /login attempts
230
233
self ._failed_login_attempts_ratelimiter = Ratelimiter (
231
234
clock = hs .get_clock (),
@@ -283,7 +286,7 @@ async def validate_user_via_ui_auth(
283
286
request_body : Dict [str , Any ],
284
287
clientip : str ,
285
288
description : str ,
286
- ) -> Tuple [dict , str ]:
289
+ ) -> Tuple [dict , Optional [ str ] ]:
287
290
"""
288
291
Checks that the user is who they claim to be, via a UI auth.
289
292
@@ -310,7 +313,8 @@ async def validate_user_via_ui_auth(
310
313
have been given only in a previous call).
311
314
312
315
'session_id' is the ID of this session, either passed in by the
313
- client or assigned by this call
316
+ client or assigned by this call. This is None if UI auth was
317
+ skipped (by re-using a previous validation).
314
318
315
319
Raises:
316
320
InteractiveAuthIncompleteError if the client has not yet completed
@@ -324,6 +328,16 @@ async def validate_user_via_ui_auth(
324
328
325
329
"""
326
330
331
+ if self ._ui_auth_session_timeout :
332
+ last_validated = await self .store .get_access_token_last_validated (
333
+ requester .access_token_id
334
+ )
335
+ if self .clock .time_msec () - last_validated < self ._ui_auth_session_timeout :
336
+ # Return the input parameters, minus the auth key, which matches
337
+ # the logic in check_ui_auth.
338
+ request_body .pop ("auth" , None )
339
+ return request_body , None
340
+
327
341
user_id = requester .user .to_string ()
328
342
329
343
# Check if we should be ratelimited due to too many previous failed attempts
@@ -359,6 +373,9 @@ async def validate_user_via_ui_auth(
359
373
if user_id != requester .user .to_string ():
360
374
raise AuthError (403 , "Invalid auth" )
361
375
376
+ # Note that the access token has been validated.
377
+ await self .store .update_access_token_last_validated (requester .access_token_id )
378
+
362
379
return params , session_id
363
380
364
381
async def _get_available_ui_auth_types (self , user : UserID ) -> Iterable [str ]:
@@ -452,13 +469,10 @@ async def check_ui_auth(
452
469
all the stages in any of the permitted flows.
453
470
"""
454
471
455
- authdict = None
456
472
sid = None # type: Optional[str]
457
- if clientdict and "auth" in clientdict :
458
- authdict = clientdict ["auth" ]
459
- del clientdict ["auth" ]
460
- if "session" in authdict :
461
- sid = authdict ["session" ]
473
+ authdict = clientdict .pop ("auth" , {})
474
+ if "session" in authdict :
475
+ sid = authdict ["session" ]
462
476
463
477
# Convert the URI and method to strings.
464
478
uri = request .uri .decode ("utf-8" )
@@ -563,6 +577,8 @@ async def check_ui_auth(
563
577
564
578
creds = await self .store .get_completed_ui_auth_stages (session .session_id )
565
579
for f in flows :
580
+ # If all the required credentials have been supplied, the user has
581
+ # successfully completed the UI auth process!
566
582
if len (set (f ) - set (creds )) == 0 :
567
583
# it's very useful to know what args are stored, but this can
568
584
# include the password in the case of registering, so only log
0 commit comments