5
5
6
6
use mas_axum_utils:: cookies:: CookieJar ;
7
7
use mas_router:: UrlBuilder ;
8
- use mas_storage:: {
9
- upstream_oauth2:: UpstreamOAuthProviderRepository , RepositoryAccess
10
- } ;
8
+ use mas_storage:: { RepositoryAccess , upstream_oauth2:: UpstreamOAuthProviderRepository } ;
11
9
use serde:: { Deserialize , Serialize } ;
12
- use tracing:: { info, error} ;
13
- use url:: Url ;
14
- use crate :: impl_from_error_for_route;
15
10
use thiserror:: Error ;
11
+ use tracing:: { error, warn} ;
12
+ use url:: Url ;
16
13
17
14
use super :: UpstreamSessionsCookie ;
15
+ use crate :: impl_from_error_for_route;
18
16
19
17
#[ derive( Serialize , Deserialize ) ]
20
18
struct LogoutToken {
@@ -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
}
@@ -60,87 +57,92 @@ impl From<reqwest::Error> for RouteError {
60
57
///
61
58
/// * `repo`: The repository to use
62
59
/// * `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
+ ///
66
62
/// # Returns
67
63
///
68
64
/// Information about upstream logout endpoints the user should be redirected to
69
65
///
70
66
/// # Errors
71
67
///
72
- /// Returns a RouteError if there's an issue accessing the repository
68
+ /// Returns a ` RouteError` if there's an issue accessing the repository
73
69
pub async fn get_rp_initiated_logout_endpoints < E > (
74
70
url_builder : & UrlBuilder ,
75
71
repo : & mut impl RepositoryAccess < Error = E > ,
76
72
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 > ,
78
76
{
79
77
let mut result: UpstreamLogoutInfo = UpstreamLogoutInfo :: default ( ) ;
80
-
81
78
// Set the post-logout redirect URI to our app's logout completion page
82
79
let post_logout_redirect_uri = url_builder
83
80
. absolute_url_for ( & mas_router:: Login :: default ( ) )
84
81
. to_string ( ) ;
85
82
result. post_logout_redirect_uri = Some ( post_logout_redirect_uri. clone ( ) ) ;
86
83
87
- let sessions_cookie = UpstreamSessionsCookie :: load ( & cookie_jar) ;
88
-
84
+ let sessions_cookie = UpstreamSessionsCookie :: load ( cookie_jar) ;
89
85
// Standard location for OIDC end session endpoint
90
86
let session_ids = sessions_cookie. session_ids ( ) ;
91
87
if session_ids. is_empty ( ) {
92
88
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 ) ?;
101
106
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
+ }
106
113
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) ;
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 . 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
+ ) ;
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