33// SPDX-License-Identifier: AGPL-3.0-only
44// Please see LICENSE in the repository root for full details.
55
6+ use crate :: impl_from_error_for_route;
67use mas_axum_utils:: cookies:: CookieJar ;
78use mas_router:: UrlBuilder ;
8- use mas_storage:: {
9- upstream_oauth2:: UpstreamOAuthProviderRepository , RepositoryAccess
10- } ;
9+ use mas_storage:: { RepositoryAccess , upstream_oauth2:: UpstreamOAuthProviderRepository } ;
1110use serde:: { Deserialize , Serialize } ;
12- use tracing:: { info, error} ;
13- use url:: Url ;
14- use crate :: impl_from_error_for_route;
1511use thiserror:: Error ;
12+ use tracing:: { error, warn} ;
13+ use url:: Url ;
1614
1715use super :: UpstreamSessionsCookie ;
1816
@@ -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,78 @@ 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 provider
119+ // For now, we'll try to construct one from the issuer if available
120+ if let Some ( issuer) = & provider. issuer {
121+ let end_session_endpoint = format ! ( "{}/protocol/openid-connect/logout" , issuer) ;
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 = logout_url. clone ( ) ;
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