55
66use mas_axum_utils:: cookies:: CookieJar ;
77use mas_router:: UrlBuilder ;
8- use mas_storage:: {
9- upstream_oauth2:: UpstreamOAuthProviderRepository , RepositoryAccess
10- } ;
8+ use mas_storage:: { RepositoryAccess , upstream_oauth2:: UpstreamOAuthProviderRepository } ;
119use serde:: { Deserialize , Serialize } ;
12- use tracing:: { info, error} ;
13- use url:: Url ;
14- use crate :: impl_from_error_for_route;
1510use thiserror:: Error ;
11+ use tracing:: { error, warn} ;
12+ use url:: Url ;
1613
1714use super :: UpstreamSessionsCookie ;
15+ use crate :: impl_from_error_for_route;
1816
1917#[ derive( Serialize , Deserialize ) ]
2018struct LogoutToken {
@@ -26,7 +24,6 @@ struct LogoutToken {
2624pub struct UpstreamLogoutInfo {
2725 /// Collection of logout endpoints that the user needs to be redirected to
2826 pub logout_endpoints : String ,
29-
3027 /// Optional post-logout redirect URI to come back to our app
3128 pub post_logout_redirect_uri : Option < String > ,
3229}
@@ -62,7 +59,7 @@ impl From<reqwest::Error> for RouteError {
6259/// * `url_builder`: URL builder for constructing redirect URIs
6360/// * `session`: The browser session to log out
6461/// * `grant_id`: Optional grant ID to use for generating id_token_hint
65- ///
62+ ///
6663/// # Returns
6764///
6865/// Information about upstream logout endpoints the user should be redirected to
@@ -74,73 +71,79 @@ pub async fn get_rp_initiated_logout_endpoints<E>(
7471 url_builder : & UrlBuilder ,
7572 repo : & mut impl RepositoryAccess < Error = E > ,
7673 cookie_jar : & CookieJar ,
77- ) -> Result < UpstreamLogoutInfo , RouteError > where RouteError : std:: convert:: From < E >
74+ ) -> Result < UpstreamLogoutInfo , RouteError >
75+ where
76+ RouteError : std:: convert:: From < E > ,
7877{
7978 let mut result: UpstreamLogoutInfo = UpstreamLogoutInfo :: default ( ) ;
80-
8179 // Set the post-logout redirect URI to our app's logout completion page
8280 let post_logout_redirect_uri = url_builder
8381 . absolute_url_for ( & mas_router:: Login :: default ( ) )
8482 . to_string ( ) ;
8583 result. post_logout_redirect_uri = Some ( post_logout_redirect_uri. clone ( ) ) ;
8684
8785 let sessions_cookie = UpstreamSessionsCookie :: load ( & cookie_jar) ;
88-
8986 // Standard location for OIDC end session endpoint
9087 let session_ids = sessions_cookie. session_ids ( ) ;
9188 if session_ids. is_empty ( ) {
9289 return Ok ( result) ;
93- }
94- // We only support the first upstrea session at a time for now
95- let upstream_session_id = session_ids[ 0 ] ;
96- let upstream_session = repo
97- . upstream_oauth_session ( )
98- . lookup ( upstream_session_id)
99- . await ?
100- . ok_or ( RouteError :: SessionNotFound ) ?;
90+ }
91+ // We only support the first upstream session
92+ let mut provider = None ;
93+ let mut upstream_session = None ;
94+ for session_id in session_ids {
95+ // Get the session and assign its value, wrapped in Some
96+ let session = repo
97+ . upstream_oauth_session ( )
98+ . lookup ( session_id)
99+ . await ?
100+ . ok_or ( RouteError :: SessionNotFound ) ?;
101+ // Get the provider and assign its value, wrapped in Some
102+ let prov = repo
103+ . upstream_oauth_provider ( )
104+ . lookup ( session. provider_id )
105+ . await ?
106+ . ok_or ( RouteError :: ProviderNotFound ) ?;
101107
102- let provider = repo. upstream_oauth_provider ( )
103- . lookup ( upstream_session. provider_id )
104- . await ?
105- . ok_or ( RouteError :: ProviderNotFound ) ?;
108+ if prov. allow_rp_initiated_logout {
109+ upstream_session = Some ( session) ;
110+ provider = Some ( prov) ;
111+ break ;
112+ }
113+ }
106114
107- // Look for end session endpoint
108- // In a real implementation, we'd have end_session_endpoint fields in the provider
109- // For now, we'll try to construct one from the issuer if available
110- if let Some ( issuer) = & provider. issuer {
111- let end_session_endpoint = format ! ( "{}/protocol/openid-connect/logout" , issuer) ;
112- let mut logout_url = end_session_endpoint;
113-
114- // Add post_logout_redirect_uri
115- if let Some ( post_uri) = & result. post_logout_redirect_uri {
116- if let Ok ( mut url) = Url :: parse ( & logout_url) {
117- url. query_pairs_mut ( )
118- . append_pair ( "post_logout_redirect_uri" , post_uri) ;
119- url. query_pairs_mut ( )
120- . append_pair ( "client_id" , & provider. client_id ) ;
121-
122- // Add id_token_hint if available
123- if upstream_session. id_token ( ) . is_some ( ) {
115+ // Check if we found a provider with allow_rp_initiated_logout
116+ if let Some ( provider) = provider {
117+ // Look for end session endpoint
118+ // In a real implementation, we'd have end_session_endpoint fields in the
119+ // provider For now, we'll try to construct one from the issuer if
120+ // available
121+ if let Some ( issuer) = & provider. issuer {
122+ let end_session_endpoint = format ! ( "{}/protocol/openid-connect/logout" , issuer) ;
123+ let mut logout_url = end_session_endpoint;
124+ // Add post_logout_redirect_uri
125+ if let Some ( post_uri) = & result. post_logout_redirect_uri {
126+ if let Ok ( mut url) = Url :: parse ( & logout_url) {
127+ url. query_pairs_mut ( )
128+ . append_pair ( "post_logout_redirect_uri" , post_uri) ;
124129 url. query_pairs_mut ( )
125- . append_pair ( "id_token_hint" , upstream_session. id_token ( ) . unwrap ( ) ) ;
130+ . append_pair ( "client_id" , & provider. client_id ) ;
131+ // Add id_token_hint if available
132+ if let Some ( session) = & upstream_session {
133+ if let Some ( id_token) = session. id_token ( ) {
134+ url. query_pairs_mut ( ) . append_pair ( "id_token_hint" , id_token) ;
135+ }
136+ }
137+ logout_url = url. to_string ( ) ;
126138 }
127- logout_url = url. to_string ( ) ;
128139 }
140+ result. logout_endpoints = logout_url. clone ( ) ;
141+ } else {
142+ warn ! (
143+ upstream_oauth_provider. id = %provider. id,
144+ "Provider has no issuer defined, cannot construct RP-initiated logout URL"
145+ ) ;
129146 }
130-
131- info ! (
132- upstream_oauth_provider. id = %provider. id,
133- logout_url = %logout_url,
134- "Adding RP-initiated logout URL based on issuer"
135- ) ;
136-
137- result. logout_endpoints = logout_url. clone ( ) ;
138- } else {
139- info ! (
140- upstream_oauth_provider. id = %provider. id,
141- "Provider has no issuer defined, cannot construct RP-initiated logout URL"
142- ) ;
143147 }
144-
145148 Ok ( result)
146149}
0 commit comments