2020from utils .core .auth import (
2121 HTML_PASSWORD_PATTERN ,
2222 COMPILED_PASSWORD_PATTERN ,
23- COOKIE_SECURE ,
2423 MAX_EMAILS_PER_ACCOUNT ,
2524 oauth2_scheme_cookie ,
2625 get_password_hash ,
2726 create_access_token ,
2827 create_tracked_refresh_token ,
2928 revoke_all_refresh_tokens ,
3029 validate_token ,
30+ set_auth_cookies ,
31+ clear_auth_cookies ,
3132 send_reset_email_task ,
3233 send_email_verification ,
3334 send_email_verified_notification ,
@@ -127,8 +128,7 @@ def logout(
127128 Log out a user by revoking their refresh token and clearing cookies.
128129 """
129130 response = RedirectResponse (url = "/" , status_code = 303 )
130- response .delete_cookie ("access_token" )
131- response .delete_cookie ("refresh_token" )
131+ clear_auth_cookies (response )
132132
133133 _ , refresh_token_value = tokens
134134 if refresh_token_value :
@@ -382,7 +382,9 @@ async def register(
382382
383383 # Create access token using the committed account's email
384384 access_token = create_access_token (data = {"sub" : account .email , "fresh" : True })
385- refresh_token = create_tracked_refresh_token (account .id , account .email , session )
385+ refresh_token = create_tracked_refresh_token (
386+ account .id , account .email , session , persistent = False
387+ )
386388 session .commit ()
387389
388390 # Set cookie — use HX-Redirect for HTMX, 303 for regular form submissions
@@ -391,19 +393,11 @@ async def register(
391393 response .headers ["HX-Redirect" ] = str (redirect_url )
392394 else :
393395 response = RedirectResponse (url = str (redirect_url ), status_code = 303 )
394- response .set_cookie (
395- key = "access_token" ,
396- value = access_token ,
397- httponly = True ,
398- secure = COOKIE_SECURE ,
399- samesite = "strict" ,
400- )
401- response .set_cookie (
402- key = "refresh_token" ,
403- value = refresh_token ,
404- httponly = True ,
405- secure = COOKIE_SECURE ,
406- samesite = "strict" ,
396+ set_auth_cookies (
397+ response ,
398+ access_token ,
399+ refresh_token ,
400+ persistent = False ,
407401 )
408402
409403 return response
@@ -417,6 +411,7 @@ async def login(
417411 account_and_session : Tuple [Account , Session ] = Depends (
418412 get_account_from_credentials
419413 ),
414+ remember : Optional [str ] = Form (None ),
420415 invitation_token : Optional [str ] = Form (
421416 None ,
422417 title = "Invitation token" ,
@@ -507,8 +502,11 @@ async def login(
507502
508503 # Create access token
509504 assert account .id is not None
505+ persistent = remember == "on"
510506 access_token = create_access_token (data = {"sub" : account .email , "fresh" : True })
511- refresh_token = create_tracked_refresh_token (account .id , account .email , session )
507+ refresh_token = create_tracked_refresh_token (
508+ account .id , account .email , session , persistent = persistent
509+ )
512510 session .commit ()
513511
514512 # Set cookie — use HX-Redirect for HTMX, 303 for regular form submissions
@@ -517,19 +515,11 @@ async def login(
517515 response .headers ["HX-Redirect" ] = str (redirect_url )
518516 else :
519517 response = RedirectResponse (url = str (redirect_url ), status_code = 303 )
520- response .set_cookie (
521- key = "access_token" ,
522- value = access_token ,
523- httponly = True ,
524- secure = COOKIE_SECURE ,
525- samesite = "strict" ,
526- )
527- response .set_cookie (
528- key = "refresh_token" ,
529- value = refresh_token ,
530- httponly = True ,
531- secure = COOKIE_SECURE ,
532- samesite = "strict" ,
518+ set_auth_cookies (
519+ response ,
520+ access_token ,
521+ refresh_token ,
522+ persistent = persistent ,
533523 )
534524
535525 return response
@@ -553,8 +543,7 @@ async def refresh_token(
553543 response = RedirectResponse (
554544 url = router .url_path_for ("read_login" ), status_code = 303
555545 )
556- response .delete_cookie ("access_token" )
557- response .delete_cookie ("refresh_token" )
546+ clear_auth_cookies (response )
558547 return response
559548
560549 # Validate JTI server-side
@@ -563,8 +552,7 @@ async def refresh_token(
563552 response = RedirectResponse (
564553 url = router .url_path_for ("read_login" ), status_code = 303
565554 )
566- response .delete_cookie ("access_token" )
567- response .delete_cookie ("refresh_token" )
555+ clear_auth_cookies (response )
568556 return response
569557
570558 user_email = decoded_token .get ("sub" )
@@ -591,32 +579,26 @@ async def refresh_token(
591579 response = RedirectResponse (
592580 url = router .url_path_for ("read_login" ), status_code = 303
593581 )
594- response .delete_cookie ("access_token" )
595- response .delete_cookie ("refresh_token" )
582+ clear_auth_cookies (response )
596583 return response
597584
598585 # Revoke current token and issue new ones
599586 db_token .revoked = True
587+ persistent = bool (decoded_token .get ("persistent" , False ))
600588 new_access_token = create_access_token (data = {"sub" : account .email , "fresh" : False })
601- new_refresh_token = create_tracked_refresh_token (account .id , account .email , session )
589+ new_refresh_token = create_tracked_refresh_token (
590+ account .id , account .email , session , persistent = persistent
591+ )
602592 session .commit ()
603593
604594 response = RedirectResponse (
605595 url = dashboard_router .url_path_for ("read_dashboard" ), status_code = 303
606596 )
607- response .set_cookie (
608- key = "access_token" ,
609- value = new_access_token ,
610- httponly = True ,
611- secure = COOKIE_SECURE ,
612- samesite = "strict" ,
613- )
614- response .set_cookie (
615- key = "refresh_token" ,
616- value = new_refresh_token ,
617- httponly = True ,
618- secure = COOKIE_SECURE ,
619- samesite = "strict" ,
597+ set_auth_cookies (
598+ response ,
599+ new_access_token ,
600+ new_refresh_token ,
601+ persistent = persistent ,
620602 )
621603
622604 return response
@@ -696,7 +678,7 @@ async def reset_password(
696678 data = {"sub" : authorized_account .email , "fresh" : True }
697679 )
698680 refresh_token = create_tracked_refresh_token (
699- authorized_account .id , authorized_account .email , session
681+ authorized_account .id , authorized_account .email , session , persistent = False
700682 )
701683 session .commit ()
702684
@@ -709,19 +691,11 @@ async def reset_password(
709691 else :
710692 response = RedirectResponse (url = redirect_url , status_code = 303 )
711693
712- response .set_cookie (
713- key = "access_token" ,
714- value = access_token ,
715- httponly = True ,
716- secure = COOKIE_SECURE ,
717- samesite = "strict" ,
718- )
719- response .set_cookie (
720- key = "refresh_token" ,
721- value = refresh_token ,
722- httponly = True ,
723- secure = COOKIE_SECURE ,
724- samesite = "strict" ,
694+ set_auth_cookies (
695+ response ,
696+ access_token ,
697+ refresh_token ,
698+ persistent = False ,
725699 )
726700 set_flash_cookie (response , message )
727701 return response
@@ -961,7 +935,9 @@ async def promote_email(
961935
962936 # Issue new tokens with the new primary email
963937 access_token = create_access_token (data = {"sub" : account .email , "fresh" : True })
964- refresh_token = create_tracked_refresh_token (account .id , account .email , session )
938+ refresh_token = create_tracked_refresh_token (
939+ account .id , account .email , session , persistent = False
940+ )
965941 session .commit ()
966942
967943 # Create recovery token and send notification to the old primary
@@ -979,18 +955,11 @@ async def promote_email(
979955 else :
980956 response = RedirectResponse (url = str (profile_path ), status_code = 303 )
981957 set_flash_cookie (response , "Primary email address updated." )
982- response .set_cookie (
983- key = "access_token" ,
984- value = access_token ,
985- httponly = True ,
986- secure = COOKIE_SECURE ,
987- samesite = "lax" ,
988- )
989- response .set_cookie (
990- key = "refresh_token" ,
991- value = refresh_token ,
992- httponly = True ,
993- secure = COOKIE_SECURE ,
958+ set_auth_cookies (
959+ response ,
960+ access_token ,
961+ refresh_token ,
962+ persistent = False ,
994963 samesite = "lax" ,
995964 )
996965 return response
0 commit comments