@@ -51,6 +51,7 @@ const OIDC_CLIENT_MIN_REFRESH_INTERVAL: Duration = Duration::from_secs(5);
5151const OIDC_HTTP_BODY_TIMEOUT : Duration = OIDC_CLIENT_MIN_REFRESH_INTERVAL ;
5252const SQLPAGE_OIDC_REDIRECT_COUNT_COOKIE : & str = "sqlpage_oidc_redirect_count" ;
5353const MAX_OIDC_REDIRECTS : u8 = 3 ;
54+ const MAX_OIDC_PARALLEL_LOGIN_FLOWS : usize = 8 ;
5455const AUTH_COOKIE_EXPIRATION : awc:: cookie:: time:: Duration =
5556 actix_web:: cookie:: time:: Duration :: days ( 7 ) ;
5657const LOGIN_FLOW_STATE_COOKIE_EXPIRATION : awc:: cookie:: time:: Duration =
@@ -455,7 +456,8 @@ fn handle_unauthenticated_request(
455456
456457 let initial_url = request. uri ( ) . to_string ( ) ;
457458 let redirect_count = get_redirect_count ( & request) ;
458- let response = build_auth_provider_redirect_response ( oidc_state, & initial_url, redirect_count) ;
459+ let response =
460+ build_auth_provider_redirect_response ( oidc_state, & request, & initial_url, redirect_count) ;
459461 MiddlewareResponse :: Respond ( request. into_response ( response) )
460462}
461463
@@ -487,7 +489,7 @@ fn handle_oidc_callback_error(
487489 if let Ok ( http_client) = get_http_client_from_appdata ( & request) {
488490 oidc_state. maybe_refresh ( http_client, OIDC_CLIENT_MIN_REFRESH_INTERVAL ) ;
489491 }
490- let resp = build_auth_provider_redirect_response ( oidc_state, "/" , redirect_count) ;
492+ let resp = build_auth_provider_redirect_response ( oidc_state, & request , "/" , redirect_count) ;
491493 request. into_response ( resp)
492494}
493495
@@ -585,7 +587,6 @@ fn process_oidc_logout(
585587 . path ( "/" )
586588 . finish ( ) ,
587589 ) ?;
588-
589590 log:: debug!( "User logged out successfully" ) ;
590591 Ok ( response)
591592}
@@ -736,6 +737,7 @@ fn set_auth_cookie(response: &mut HttpResponse, id_token: &OidcToken) {
736737
737738fn build_auth_provider_redirect_response (
738739 oidc_state : & OidcState ,
740+ request : & ServiceRequest ,
739741 initial_url : & str ,
740742 redirect_count : u8 ,
741743) -> HttpResponse {
@@ -750,11 +752,15 @@ fn build_auth_provider_redirect_response(
750752 . same_site ( actix_web:: cookie:: SameSite :: Lax )
751753 . max_age ( LOGIN_FLOW_STATE_COOKIE_EXPIRATION )
752754 . finish ( ) ;
753- HttpResponse :: SeeOther ( )
754- . append_header ( ( header:: LOCATION , url. to_string ( ) ) )
755- . cookie ( tmp_login_flow_state_cookie)
756- . cookie ( redirect_count_cookie)
757- . body ( "Redirecting..." )
755+ let mut response = HttpResponse :: SeeOther ( ) ;
756+ response. append_header ( ( header:: LOCATION , url. to_string ( ) ) ) ;
757+ for mut cookie in get_tmp_login_flow_state_cookies_to_evict ( request) {
758+ cookie. make_removal ( ) ;
759+ response. cookie ( cookie) ;
760+ }
761+ response. cookie ( tmp_login_flow_state_cookie) ;
762+ response. cookie ( redirect_count_cookie) ;
763+ response. body ( "Redirecting..." )
758764}
759765
760766fn build_redirect_response ( target_url : String ) -> HttpResponse {
@@ -1078,6 +1084,38 @@ fn get_tmp_login_flow_state_cookie(
10781084 . with_context ( || format ! ( "No {cookie_name} cookie found" ) )
10791085}
10801086
1087+ fn get_tmp_login_flow_state_cookies_to_evict ( request : & ServiceRequest ) -> Vec < Cookie < ' static > > {
1088+ request
1089+ . cookies ( )
1090+ . map ( |cookies| {
1091+ let excess_cookie_count = cookies
1092+ . iter ( )
1093+ . filter ( |cookie| {
1094+ cookie
1095+ . name ( )
1096+ . starts_with ( SQLPAGE_TMP_LOGIN_STATE_COOKIE_PREFIX )
1097+ } )
1098+ . count ( )
1099+ . saturating_add ( 1 )
1100+ . saturating_sub ( MAX_OIDC_PARALLEL_LOGIN_FLOWS ) ;
1101+ cookies
1102+ . iter ( )
1103+ . filter ( |cookie| {
1104+ cookie
1105+ . name ( )
1106+ . starts_with ( SQLPAGE_TMP_LOGIN_STATE_COOKIE_PREFIX )
1107+ } )
1108+ . take ( excess_cookie_count)
1109+ . map ( |cookie| {
1110+ let mut cookie = cookie. clone ( ) . into_owned ( ) ;
1111+ cookie. set_path ( "/" ) ;
1112+ cookie
1113+ } )
1114+ . collect ( )
1115+ } )
1116+ . unwrap_or_default ( )
1117+ }
1118+
10811119#[ derive( Debug , Serialize , Deserialize , Clone ) ]
10821120struct LoginFlowState < ' a > {
10831121 #[ serde( rename = "n" ) ]
@@ -1127,6 +1165,7 @@ fn validate_redirect_url(url: String, redirect_uri: &str) -> String {
11271165mod tests {
11281166 use super :: * ;
11291167 use actix_web:: http:: StatusCode ;
1168+ use actix_web:: { cookie:: Cookie , test:: TestRequest } ;
11301169 use openidconnect:: url:: Url ;
11311170
11321171 #[ test]
@@ -1182,4 +1221,23 @@ mod tests {
11821221 . expect ( "generated URL should parse" ) ;
11831222 verify_logout_params ( & params, secret) . expect ( "generated URL should validate" ) ;
11841223 }
1224+
1225+ #[ test]
1226+ fn evicts_excess_tmp_login_flow_state_cookies ( ) {
1227+ let request = ( 0 ..MAX_OIDC_PARALLEL_LOGIN_FLOWS )
1228+ . fold ( TestRequest :: default ( ) , |request, i| {
1229+ request. cookie ( Cookie :: new (
1230+ format ! ( "{SQLPAGE_TMP_LOGIN_STATE_COOKIE_PREFIX}{i}" ) ,
1231+ format ! ( "value-{i}" ) ,
1232+ ) )
1233+ } )
1234+ . to_srv_request ( ) ;
1235+
1236+ let cookies_to_evict = get_tmp_login_flow_state_cookies_to_evict ( & request) ;
1237+
1238+ assert_eq ! ( cookies_to_evict. len( ) , 1 ) ;
1239+ assert ! ( cookies_to_evict[ 0 ]
1240+ . name( )
1241+ . starts_with( SQLPAGE_TMP_LOGIN_STATE_COOKIE_PREFIX ) ) ;
1242+ }
11851243}
0 commit comments