Skip to content

Commit 4a6c1d6

Browse files
committed
Merge: resolve conflict in oidc tests, keeping both test functions
2 parents 3aa95a4 + 72eff80 commit 4a6c1d6

2 files changed

Lines changed: 72 additions & 13 deletions

File tree

src/webserver/oidc.rs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,6 @@ fn get_app_host(config: &AppConfig) -> String {
157157
host
158158
}
159159

160-
fn build_absolute_uri(app_host: &str, relative_path: &str, scheme: &str) -> anyhow::Result<String> {
161-
let mut base_url = Url::parse(&format!("{scheme}://{app_host}"))
162-
.with_context(|| format!("Failed to parse app_host: {app_host}"))?;
163-
base_url.set_path("");
164-
let absolute_url = base_url
165-
.join(relative_path)
166-
.with_context(|| format!("Failed to join path {relative_path}"))?;
167-
Ok(absolute_url.to_string())
168-
}
169-
170160
pub struct ClientWithTime {
171161
client: OidcClient,
172162
end_session_endpoint: Option<EndSessionUrl>,
@@ -248,6 +238,29 @@ impl OidcState {
248238
.map_err(|e| anyhow::anyhow!("Could not verify the ID token: {e}"))?;
249239
Ok(claims)
250240
}
241+
242+
/// Builds an absolute redirect URI by joining the relative redirect URI with the client's redirect URL
243+
pub async fn build_absolute_redirect_uri(
244+
&self,
245+
relative_redirect_uri: &str,
246+
) -> anyhow::Result<String> {
247+
let client_guard = self.get_client().await;
248+
let client_redirect_url = client_guard
249+
.redirect_uri()
250+
.ok_or_else(|| anyhow!("OIDC client has no redirect URL configured"))?;
251+
let absolute_redirect_uri = client_redirect_url
252+
.url()
253+
.join(relative_redirect_uri)
254+
.with_context(|| {
255+
format!(
256+
"Failed to join redirect URI {} with client redirect URL {}",
257+
relative_redirect_uri,
258+
client_redirect_url.url()
259+
)
260+
})?
261+
.to_string();
262+
Ok(absolute_redirect_uri)
263+
}
251264
}
252265

253266
pub async fn initialize_oidc_state(
@@ -507,11 +520,12 @@ async fn process_oidc_logout(
507520
.ok()
508521
.flatten();
509522

510-
let scheme = request.connection_info().scheme().to_string();
511523
let mut response =
512524
if let Some(end_session_endpoint) = oidc_state.get_end_session_endpoint().await {
513-
let absolute_redirect_uri =
514-
build_absolute_uri(&oidc_state.config.app_host, &params.redirect_uri, &scheme)?;
525+
let absolute_redirect_uri = oidc_state
526+
.build_absolute_redirect_uri(&params.redirect_uri)
527+
.await?;
528+
515529
let post_logout_redirect_uri =
516530
PostLogoutRedirectUrl::new(absolute_redirect_uri.clone()).with_context(|| {
517531
format!("Invalid post_logout_redirect_uri: {absolute_redirect_uri}")

tests/oidc/mod.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct DiscoveryResponse {
6464
response_types_supported: Vec<String>,
6565
subject_types_supported: Vec<String>,
6666
id_token_signing_alg_values_supported: Vec<String>,
67+
end_session_endpoint: String,
6768
}
6869

6970
#[derive(Serialize)]
@@ -89,6 +90,7 @@ async fn discovery_endpoint(state: Data<SharedProviderState>) -> impl Responder
8990
response_types_supported: vec!["code".to_string()],
9091
subject_types_supported: vec!["public".to_string()],
9192
id_token_signing_alg_values_supported: vec!["HS256".to_string()],
93+
end_session_endpoint: format!("{}/logout", state.issuer_url),
9294
};
9395
HttpResponse::Ok()
9496
.insert_header((header::CONTENT_TYPE, "application/json"))
@@ -500,3 +502,46 @@ async fn test_oidc_with_site_prefix() {
500502
redirect_uri
501503
);
502504
}
505+
506+
#[actix_web::test]
507+
async fn test_oidc_logout_uses_correct_scheme() {
508+
use sqlpage::{
509+
app_config::{test_database_url, AppConfig},
510+
webserver::oidc::create_logout_url,
511+
AppState,
512+
};
513+
514+
crate::common::init_log();
515+
let provider = FakeOidcProvider::new().await;
516+
517+
let db_url = test_database_url();
518+
let config_json = format!(
519+
r#"{{
520+
"database_url": "{db_url}",
521+
"oidc_issuer_url": "{}",
522+
"oidc_client_id": "{}",
523+
"oidc_client_secret": "{}",
524+
"https_domain": "example.com"
525+
}}"#,
526+
provider.issuer_url, provider.client_id, provider.client_secret
527+
);
528+
529+
let config: AppConfig = serde_json::from_str(&config_json).unwrap();
530+
let app_state = AppState::init(&config).await.unwrap();
531+
let app = test::init_service(create_app(Data::new(app_state))).await;
532+
533+
let logout_path = create_logout_url("/logged_out", "", &provider.client_secret);
534+
// make sure the logout path includes the configured domain
535+
assert!(logout_path.starts_with("/sqlpage/oidc_logout"));
536+
537+
let req = test::TestRequest::get().uri(&logout_path).to_request();
538+
let resp = test::call_service(&app, req).await;
539+
540+
assert_eq!(resp.status(), StatusCode::SEE_OTHER);
541+
let location = resp.headers().get("location").unwrap().to_str().unwrap();
542+
let location_url = Url::parse(location).unwrap();
543+
assert_eq!(location_url.path(), "/logout");
544+
let params: HashMap<String, String> = location_url.query_pairs().into_owned().collect();
545+
let post_logout = params.get("post_logout_redirect_uri").unwrap();
546+
assert_eq!(post_logout, "https://example.com/logged_out");
547+
}

0 commit comments

Comments
 (0)