fix(auth): align OAuth metadata discovery ordering (#887)
This commit is contained in:
parent
443677ca31
commit
3c5ce2b0d7
2 changed files with 47 additions and 1 deletions
|
|
@ -1508,7 +1508,7 @@ impl AuthorizationManager {
|
||||||
push_candidate("/.well-known/oauth-authorization-server".to_string());
|
push_candidate("/.well-known/oauth-authorization-server".to_string());
|
||||||
push_candidate("/.well-known/openid-configuration".to_string());
|
push_candidate("/.well-known/openid-configuration".to_string());
|
||||||
} else {
|
} 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
|
// 1. OAuth 2.0 with path insertion
|
||||||
push_candidate(format!("/.well-known/oauth-authorization-server/{trimmed}"));
|
push_candidate(format!("/.well-known/oauth-authorization-server/{trimmed}"));
|
||||||
// 2. OpenID Connect with path insertion
|
// 2. OpenID Connect with path insertion
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,25 @@ async fn start_mock_server() -> (String, SocketAddr) {
|
||||||
(base_url, addr)
|
(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]
|
#[tokio::test]
|
||||||
async fn test_client_credentials_flow_client_secret() {
|
async fn test_client_credentials_flow_client_secret() {
|
||||||
let (base_url, _addr) = start_mock_server().await;
|
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");
|
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]
|
#[tokio::test]
|
||||||
async fn test_client_credentials_invalid_secret() {
|
async fn test_client_credentials_invalid_secret() {
|
||||||
let (base_url, _addr) = start_mock_server().await;
|
let (base_url, _addr) = start_mock_server().await;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue