fix(oauth): respect oauth-protected-resource discovery (#511)

This commit is contained in:
Gabriel Peal 2025-11-05 06:19:13 -08:00 • committed by GitHub
commit 82f9b0976a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -615,7 +615,7 @@ impl AuthorizationManager {
async fn discover_oauth_server_via_resource_metadata( async fn discover_oauth_server_via_resource_metadata(
&self, &self,
) -> Result<Option<AuthorizationMetadata>, AuthError> { ) -> Result<Option<AuthorizationMetadata>, AuthError> {
let Some(resource_metadata_url) = self.fetch_resource_metadata_url().await? else { let Some(resource_metadata_url) = self.discover_resource_metadata_url().await? else {
return Ok(None); return Ok(None);
}; };
@ -667,12 +667,38 @@ impl AuthorizationManager {
Ok(None) Ok(None)
} }
async fn discover_resource_metadata_url(&self) -> Result<Option<Url>, AuthError> {
if let Ok(Some(resource_metadata_url)) =
self.fetch_resource_metadata_url(&self.base_url).await
{
return Ok(Some(resource_metadata_url));
}
// If the primary URL doesn't use WWW-Authenticate, try oauth-protected-resource discovery.
// https://www.rfc-editor.org/rfc/rfc9728.html#name-obtaining-protected-resourc
for candidate_path in
Self::well_known_paths(self.base_url.path(), "oauth-protected-resource")
{
let mut discovery_url = self.base_url.clone();
discovery_url.set_query(None);
discovery_url.set_fragment(None);
discovery_url.set_path(&candidate_path);
if let Ok(Some(resource_metadata_url)) =
self.fetch_resource_metadata_url(&discovery_url).await
{
return Ok(Some(resource_metadata_url));
}
}
Ok(None)
}
/// Extract the resource metadata url from the WWW-Authenticate header value. /// Extract the resource metadata url from the WWW-Authenticate header value.
/// https://www.rfc-editor.org/rfc/rfc9728.html#name-use-of-www-authenticate-for /// https://www.rfc-editor.org/rfc/rfc9728.html#name-use-of-www-authenticate-for
async fn fetch_resource_metadata_url(&self) -> Result<Option<Url>, AuthError> { async fn fetch_resource_metadata_url(&self, url: &Url) -> Result<Option<Url>, AuthError> {
let response = match self let response = match self
.http_client .http_client
.get(self.base_url.clone()) .get(url.clone())
.header("MCP-Protocol-Version", "2024-11-05") .header("MCP-Protocol-Version", "2024-11-05")
.send() .send()
.await .await
@ -684,7 +710,9 @@ impl AuthorizationManager {
} }
}; };
if response.status() != StatusCode::UNAUTHORIZED { if response.status() == StatusCode::OK {
return Ok(Some(url.clone()));
} else if response.status() != StatusCode::UNAUTHORIZED {
debug!( debug!(
"resource metadata probe returned unexpected status: {}", "resource metadata probe returned unexpected status: {}",
response.status() response.status()