From 2472e168b33276a78880e761c3a019dd45425245 Mon Sep 17 00:00:00 2001 From: King Star Date: Thu, 4 Jun 2026 21:41:23 +0800 Subject: [PATCH] fix(auth): align OAuth metadata discovery ordering --- crates/rmcp/src/transport/auth.rs | 2 +- crates/rmcp/tests/test_client_credentials.rs | 46 ++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/crates/rmcp/src/transport/auth.rs b/crates/rmcp/src/transport/auth.rs index 3c0b5583..baded685 100644 --- a/crates/rmcp/src/transport/auth.rs +++ b/crates/rmcp/src/transport/auth.rs @@ -1409,7 +1409,7 @@ impl AuthorizationManager { push_candidate("/.well-known/oauth-authorization-server".to_string()); push_candidate("/.well-known/openid-configuration".to_string()); } else { - // Path components present: follow spec priority order + // Path components present: prefer OAuth discovery before OpenID Connect fallbacks. // 1. OAuth 2.0 with path insertion push_candidate(format!("/.well-known/oauth-authorization-server/{trimmed}")); // 2. OpenID Connect with path insertion diff --git a/crates/rmcp/tests/test_client_credentials.rs b/crates/rmcp/tests/test_client_credentials.rs index a90b8b0e..b2698d1b 100644 --- a/crates/rmcp/tests/test_client_credentials.rs +++ b/crates/rmcp/tests/test_client_credentials.rs @@ -136,6 +136,25 @@ async fn start_mock_server() -> (String, SocketAddr) { (base_url, addr) } +async fn start_path_insert_metadata_server() -> (String, SocketAddr) { + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let base_url = format!("http://{}", addr); + + let app = Router::new() + .route( + "/.well-known/oauth-authorization-server/mcp", + get(auth_server_metadata_handler), + ) + .route("/token", post(token_handler)); + + tokio::spawn(async move { + axum::serve(listener, app).await.unwrap(); + }); + + (base_url, addr) +} + #[tokio::test] async fn test_client_credentials_flow_client_secret() { let (base_url, _addr) = start_mock_server().await; @@ -162,6 +181,33 @@ async fn test_client_credentials_flow_client_secret() { assert_eq!(token, "m2m-access-token-12345"); } +#[tokio::test] +async fn test_client_credentials_discovers_path_inserted_oauth_metadata() { + let (base_url, _addr) = start_path_insert_metadata_server().await; + let resource_url = format!("{base_url}/mcp"); + + let mut oauth_state = OAuthState::new(&resource_url, None).await.unwrap(); + + let config = ClientCredentialsConfig::ClientSecret { + client_id: "test-m2m-client".to_string(), + client_secret: "test-m2m-secret".to_string(), + scopes: vec!["read".to_string()], + resource: Some(resource_url), + }; + + oauth_state + .authenticate_client_credentials(config) + .await + .unwrap(); + + let manager = oauth_state + .into_authorization_manager() + .expect("Should be in Authorized state"); + + let token = manager.get_access_token().await.unwrap(); + assert_eq!(token, "m2m-access-token-12345"); +} + #[tokio::test] async fn test_client_credentials_invalid_secret() { let (base_url, _addr) = start_mock_server().await;