@@ -108,10 +108,6 @@ class OAuthContext:
108108 # State
109109 lock : anyio .Lock = field (default_factory = anyio .Lock )
110110
111- # Discovery state for fallback support
112- discovery_base_url : str | None = None
113- discovery_pathname : str | None = None
114-
115111 def get_authorization_base_url (self , server_url : str ) -> str :
116112 """Extract base URL by removing path component."""
117113 parsed = urlparse (server_url )
@@ -228,23 +224,23 @@ def _extract_resource_metadata_from_www_auth(self, init_response: httpx.Response
228224
229225 return None
230226
231- async def _discover_protected_resource (self , init_response : httpx .Response ) -> httpx .Request :
232- # RFC9728: Try to extract resource_metadata URL from WWW-Authenticate header of the initial response
233- url = self ._extract_resource_metadata_from_www_auth (init_response )
234-
235- if not url :
236- # Fallback to well-known discovery with path component included
237- parsed = urlparse (self .context .server_url )
238- auth_base_url = f"{ parsed .scheme } ://{ parsed .netloc } "
227+ def _get_protected_resource_discovery_urls (self ) -> list [str ]:
228+ """Generate ordered list of URLs for protected resource discovery attempts."""
229+ urls : list [str ] = []
230+ parsed = urlparse (self .context .server_url )
231+ base_url = f"{ parsed .scheme } ://{ parsed .netloc } "
239232
240- if parsed .path and parsed .path != "/" :
241- # Include path component in the well-known URL
242- path_component = parsed .path .rstrip ("/" )
243- url = urljoin (auth_base_url , f"/.well-known/oauth-protected-resource{ path_component } " )
244- else :
245- url = urljoin (auth_base_url , "/.well-known/oauth-protected-resource" )
233+ if parsed .path and parsed .path != "/" :
234+ # Try path-specific endpoint first
235+ path_component = parsed .path .rstrip ("/" )
236+ urls .append (urljoin (base_url , f"/.well-known/oauth-protected-resource{ path_component } " ))
237+ # Then fallback to base endpoint
238+ urls .append (urljoin (base_url , "/.well-known/oauth-protected-resource" ))
239+ else :
240+ # No path, just use base endpoint
241+ urls .append (urljoin (base_url , "/.well-known/oauth-protected-resource" ))
246242
247- return httpx . Request ( "GET" , url , headers = { MCP_PROTOCOL_VERSION : LATEST_PROTOCOL_VERSION })
243+ return urls
248244
249245 async def _handle_protected_resource_response (self , response : httpx .Response ) -> None :
250246 """Handle discovery response."""
@@ -517,9 +513,28 @@ async def async_auth_flow(self, request: httpx.Request) -> AsyncGenerator[httpx.
517513 try :
518514 # OAuth flow must be inline due to generator constraints
519515 # Step 1: Discover protected resource metadata (RFC9728 with WWW-Authenticate support)
520- discovery_request = await self ._discover_protected_resource (response )
521- discovery_response = yield discovery_request
522- await self ._handle_protected_resource_response (discovery_response )
516+ # Check if WWW-Authenticate provides resource_metadata URL first
517+ www_auth_url = self ._extract_resource_metadata_from_www_auth (response )
518+ if www_auth_url :
519+ discovery_request = httpx .Request (
520+ "GET" , www_auth_url , headers = {MCP_PROTOCOL_VERSION : LATEST_PROTOCOL_VERSION }
521+ )
522+ discovery_response = yield discovery_request
523+ await self ._handle_protected_resource_response (discovery_response )
524+ else :
525+ # Try well-known discovery URLs with fallback
526+ discovery_urls = self ._get_protected_resource_discovery_urls ()
527+ for url in discovery_urls :
528+ discovery_request = httpx .Request (
529+ "GET" , url , headers = {MCP_PROTOCOL_VERSION : LATEST_PROTOCOL_VERSION }
530+ )
531+ discovery_response = yield discovery_request
532+
533+ if discovery_response .status_code == 200 :
534+ await self ._handle_protected_resource_response (discovery_response )
535+ break # Success, stop trying other URLs
536+ elif discovery_response .status_code != 404 :
537+ break # Non-404 error, stop trying
523538
524539 # Step 2: Discover OAuth metadata (with fallback for legacy servers)
525540 discovery_urls = self ._get_discovery_urls ()
0 commit comments