3
3
// SPDX-License-Identifier: AGPL-3.0-only
4
4
// Please see LICENSE in the repository root for full details.
5
5
6
+ use crate :: impl_from_error_for_route;
6
7
use mas_axum_utils:: cookies:: CookieJar ;
7
8
use mas_router:: UrlBuilder ;
8
- use mas_storage:: {
9
- upstream_oauth2:: UpstreamOAuthProviderRepository , RepositoryAccess
10
- } ;
9
+ use mas_storage:: { RepositoryAccess , upstream_oauth2:: UpstreamOAuthProviderRepository } ;
11
10
use serde:: { Deserialize , Serialize } ;
12
- use tracing:: { info, error} ;
13
- use url:: Url ;
14
- use crate :: impl_from_error_for_route;
15
11
use thiserror:: Error ;
12
+ use tracing:: { error, warn} ;
13
+ use url:: Url ;
16
14
17
15
use super :: UpstreamSessionsCookie ;
18
16
@@ -26,7 +24,6 @@ struct LogoutToken {
26
24
pub struct UpstreamLogoutInfo {
27
25
/// Collection of logout endpoints that the user needs to be redirected to
28
26
pub logout_endpoints : String ,
29
-
30
27
/// Optional post-logout redirect URI to come back to our app
31
28
pub post_logout_redirect_uri : Option < String > ,
32
29
}
@@ -62,7 +59,7 @@ impl From<reqwest::Error> for RouteError {
62
59
/// * `url_builder`: URL builder for constructing redirect URIs
63
60
/// * `session`: The browser session to log out
64
61
/// * `grant_id`: Optional grant ID to use for generating id_token_hint
65
- ///
62
+ ///
66
63
/// # Returns
67
64
///
68
65
/// Information about upstream logout endpoints the user should be redirected to
@@ -74,73 +71,78 @@ pub async fn get_rp_initiated_logout_endpoints<E>(
74
71
url_builder : & UrlBuilder ,
75
72
repo : & mut impl RepositoryAccess < Error = E > ,
76
73
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 > ,
78
77
{
79
78
let mut result: UpstreamLogoutInfo = UpstreamLogoutInfo :: default ( ) ;
80
-
81
79
// Set the post-logout redirect URI to our app's logout completion page
82
80
let post_logout_redirect_uri = url_builder
83
81
. absolute_url_for ( & mas_router:: Login :: default ( ) )
84
82
. to_string ( ) ;
85
83
result. post_logout_redirect_uri = Some ( post_logout_redirect_uri. clone ( ) ) ;
86
84
87
85
let sessions_cookie = UpstreamSessionsCookie :: load ( & cookie_jar) ;
88
-
89
86
// Standard location for OIDC end session endpoint
90
87
let session_ids = sessions_cookie. session_ids ( ) ;
91
88
if session_ids. is_empty ( ) {
92
89
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 ) ?;
101
107
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
+ }
106
114
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) ;
124
128
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 ( ) ;
126
137
}
127
- logout_url = url. to_string ( ) ;
128
138
}
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
+ ) ;
129
145
}
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
- ) ;
143
146
}
144
-
145
147
Ok ( result)
146
148
}
0 commit comments