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}
@@ -60,87 +57,92 @@ impl From<reqwest::Error> for RouteError {
6057///
6158/// * `repo`: The repository to use
6259/// * `url_builder`: URL builder for constructing redirect URIs
63- /// * `session`: The browser session to log out
64- /// * `grant_id`: Optional grant ID to use for generating id_token_hint
65- ///
60+ /// * `cookie_jar`: Cookie from user's browser session
61+ ///
6662/// # Returns
6763///
6864/// Information about upstream logout endpoints the user should be redirected to
6965///
7066/// # Errors
7167///
72- /// Returns a RouteError if there's an issue accessing the repository
68+ /// Returns a ` RouteError` if there's an issue accessing the repository
7369pub async fn get_rp_initiated_logout_endpoints < E > (
7470 url_builder : & UrlBuilder ,
7571 repo : & mut impl RepositoryAccess < Error = E > ,
7672 cookie_jar : & CookieJar ,
77- ) -> Result < UpstreamLogoutInfo , RouteError > where RouteError : std:: convert:: From < E >
73+ ) -> Result < UpstreamLogoutInfo , RouteError >
74+ where
75+ RouteError : std:: convert:: From < E > ,
7876{
7977 let mut result: UpstreamLogoutInfo = UpstreamLogoutInfo :: default ( ) ;
80-
8178 // Set the post-logout redirect URI to our app's logout completion page
8279 let post_logout_redirect_uri = url_builder
8380 . absolute_url_for ( & mas_router:: Login :: default ( ) )
8481 . to_string ( ) ;
8582 result. post_logout_redirect_uri = Some ( post_logout_redirect_uri. clone ( ) ) ;
8683
87- let sessions_cookie = UpstreamSessionsCookie :: load ( & cookie_jar) ;
88-
84+ let sessions_cookie = UpstreamSessionsCookie :: load ( cookie_jar) ;
8985 // Standard location for OIDC end session endpoint
9086 let session_ids = sessions_cookie. session_ids ( ) ;
9187 if session_ids. is_empty ( ) {
9288 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 ) ?;
89+ }
90+ // We only support the first upstream session
91+ let mut provider = None ;
92+ let mut upstream_session = None ;
93+ for session_id in session_ids {
94+ // Get the session and assign its value, wrapped in Some
95+ let session = repo
96+ . upstream_oauth_session ( )
97+ . lookup ( session_id)
98+ . await ?
99+ . ok_or ( RouteError :: SessionNotFound ) ?;
100+ // Get the provider and assign its value, wrapped in Some
101+ let prov = repo
102+ . upstream_oauth_provider ( )
103+ . lookup ( session. provider_id )
104+ . await ?
105+ . ok_or ( RouteError :: ProviderNotFound ) ?;
101106
102- let provider = repo. upstream_oauth_provider ( )
103- . lookup ( upstream_session. provider_id )
104- . await ?
105- . ok_or ( RouteError :: ProviderNotFound ) ?;
107+ if prov. allow_rp_initiated_logout {
108+ upstream_session = Some ( session) ;
109+ provider = Some ( prov) ;
110+ break ;
111+ }
112+ }
106113
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 ( ) {
114+ // Check if we found a provider with allow_rp_initiated_logout
115+ if let Some ( provider) = provider {
116+ // Look for end session endpoint
117+ // In a real implementation, we'd have end_session_endpoint fields in the
118+ // provider For now, we'll try to construct one from the issuer if
119+ // available
120+ if let Some ( issuer) = & provider. issuer {
121+ let end_session_endpoint = format ! ( "{issuer}/protocol/openid-connect/logout" ) ;
122+ let mut logout_url = end_session_endpoint;
123+ // Add post_logout_redirect_uri
124+ if let Some ( post_uri) = & result. post_logout_redirect_uri {
125+ if let Ok ( mut url) = Url :: parse ( & logout_url) {
126+ url. query_pairs_mut ( )
127+ . append_pair ( "post_logout_redirect_uri" , post_uri) ;
124128 url. query_pairs_mut ( )
125- . append_pair ( "id_token_hint" , upstream_session. id_token ( ) . unwrap ( ) ) ;
129+ . append_pair ( "client_id" , & provider. client_id ) ;
130+ // Add id_token_hint if available
131+ if let Some ( session) = & upstream_session {
132+ if let Some ( id_token) = session. id_token ( ) {
133+ url. query_pairs_mut ( ) . append_pair ( "id_token_hint" , id_token) ;
134+ }
135+ }
136+ logout_url = url. to_string ( ) ;
126137 }
127- logout_url = url. to_string ( ) ;
128138 }
139+ result. logout_endpoints . clone_from ( & logout_url) ;
140+ } else {
141+ warn ! (
142+ upstream_oauth_provider. id = %provider. id,
143+ "Provider has no issuer defined, cannot construct RP-initiated logout URL"
144+ ) ;
129145 }
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- ) ;
143146 }
144-
145147 Ok ( result)
146148}
0 commit comments