@@ -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,17 @@ 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+ if let Ok ( cookies) = request. cookies ( ) {
758+ for mut cookie in get_tmp_login_flow_state_cookies_to_evict ( & cookies) . cloned ( ) {
759+ cookie. make_removal ( ) ;
760+ response. cookie ( cookie) ;
761+ }
762+ }
763+ response. cookie ( tmp_login_flow_state_cookie) ;
764+ response. cookie ( redirect_count_cookie) ;
765+ response. body ( "Redirecting..." )
758766}
759767
760768fn build_redirect_response ( target_url : String ) -> HttpResponse {
@@ -1078,6 +1086,15 @@ fn get_tmp_login_flow_state_cookie(
10781086 . with_context ( || format ! ( "No {cookie_name} cookie found" ) )
10791087}
10801088
1089+ fn get_tmp_login_flow_state_cookies_to_evict < ' a > (
1090+ cookies : & ' a [ Cookie < ' static > ] ,
1091+ ) -> impl Iterator < Item = & ' a Cookie < ' static > > + ' a {
1092+ let is_state = & |c : & Cookie < ' _ > | c. name ( ) . starts_with ( SQLPAGE_TMP_LOGIN_STATE_COOKIE_PREFIX ) ;
1093+ let login_state_count = cookies. iter ( ) . filter ( |c| is_state ( c) ) . count ( ) ;
1094+ let to_evict = login_state_count. saturating_sub ( MAX_OIDC_PARALLEL_LOGIN_FLOWS - 1 ) ;
1095+ cookies. iter ( ) . filter ( |c| is_state ( c) ) . take ( to_evict)
1096+ }
1097+
10811098#[ derive( Debug , Serialize , Deserialize , Clone ) ]
10821099struct LoginFlowState < ' a > {
10831100 #[ serde( rename = "n" ) ]
@@ -1127,6 +1144,7 @@ fn validate_redirect_url(url: String, redirect_uri: &str) -> String {
11271144mod tests {
11281145 use super :: * ;
11291146 use actix_web:: http:: StatusCode ;
1147+ use actix_web:: { cookie:: Cookie , test:: TestRequest } ;
11301148 use openidconnect:: url:: Url ;
11311149
11321150 #[ test]
@@ -1182,4 +1200,25 @@ mod tests {
11821200 . expect ( "generated URL should parse" ) ;
11831201 verify_logout_params ( & params, secret) . expect ( "generated URL should validate" ) ;
11841202 }
1203+
1204+ #[ test]
1205+ fn evicts_excess_tmp_login_flow_state_cookies ( ) {
1206+ let request = ( 0 ..MAX_OIDC_PARALLEL_LOGIN_FLOWS )
1207+ . fold ( TestRequest :: default ( ) , |request, i| {
1208+ request. cookie ( Cookie :: new (
1209+ format ! ( "{SQLPAGE_TMP_LOGIN_STATE_COOKIE_PREFIX}{i}" ) ,
1210+ format ! ( "value-{i}" ) ,
1211+ ) )
1212+ } )
1213+ . to_srv_request ( ) ;
1214+
1215+ let cookies = request. cookies ( ) . unwrap ( ) ;
1216+ let cookies_to_evict: Vec < _ > =
1217+ get_tmp_login_flow_state_cookies_to_evict ( & cookies) . collect ( ) ;
1218+
1219+ assert_eq ! ( cookies_to_evict. len( ) , 1 ) ;
1220+ assert ! ( cookies_to_evict[ 0 ]
1221+ . name( )
1222+ . starts_with( SQLPAGE_TMP_LOGIN_STATE_COOKIE_PREFIX ) ) ;
1223+ }
11851224}
0 commit comments