Skip to content

Commit 5b3db7b

Browse files
committed
fix broken discovery paths for claude code
1 parent cb52616 commit 5b3db7b

1 file changed

Lines changed: 44 additions & 12 deletions

File tree

src/mcp_server_obp/auth.py

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,14 @@ async def oauth_authorization_server_metadata(request):
343343
status_code=500,
344344
)
345345

346+
suffix = mcp_path or ""
346347
routes.append(
347-
Route("/.well-known/oauth-authorization-server", endpoint=oauth_authorization_server_metadata, methods=["GET"])
348+
Route(f"/.well-known/oauth-authorization-server{suffix}", endpoint=oauth_authorization_server_metadata, methods=["GET"])
348349
)
350+
if suffix:
351+
routes.append(
352+
Route("/.well-known/oauth-authorization-server", endpoint=oauth_authorization_server_metadata, methods=["GET"])
353+
)
349354

350355
async def register_client_fix_auth_method(request):
351356
"""Minimal DCR proxy that fixes token_endpoint_auth_method in Keycloak's response."""
@@ -467,11 +472,12 @@ def __init__(
467472
audience=audience,
468473
)
469474

470-
# OBP-OIDC is advertised directly as the authorization server
471-
# No proxy needed - OBP-OIDC has native DCR support with correct token_endpoint_auth_method
475+
# Advertise ourselves as the authorization server so all discovery and DCR
476+
# flows route through our proxy — this lets us fix incompatibilities in
477+
# OBP-OIDC's responses (e.g. null fields in DCR) before they reach clients.
472478
super().__init__(
473479
token_verifier=token_verifier,
474-
authorization_servers=[AnyHttpUrl(self.issuer_url)],
480+
authorization_servers=[self.base_url],
475481
base_url=self.base_url,
476482
)
477483

@@ -499,6 +505,11 @@ async def forward_oauth_authorization_server_metadata(request):
499505
)
500506
response.raise_for_status()
501507
metadata = response.json()
508+
509+
# Route DCR through our proxy so we can fix null fields
510+
base_url = str(self.base_url).rstrip("/")
511+
metadata["registration_endpoint"] = f"{base_url}/register"
512+
502513
return JSONResponse(metadata)
503514
except httpx.HTTPStatusError as e:
504515
logger.error(f"Failed to fetch OBP-OIDC metadata: {e}")
@@ -523,6 +534,11 @@ async def forward_openid_configuration(request):
523534
)
524535
response.raise_for_status()
525536
metadata = response.json()
537+
538+
# Route DCR through our proxy so we can fix null fields
539+
base_url = str(self.base_url).rstrip("/")
540+
metadata["registration_endpoint"] = f"{base_url}/register"
541+
526542
return JSONResponse(metadata)
527543
except httpx.HTTPStatusError as e:
528544
logger.error(f"Failed to fetch OBP-OIDC openid-configuration: {e}")
@@ -555,11 +571,17 @@ async def forward_register(request):
555571
registration_endpoint = f"{self.issuer_url}/connect/register"
556572
response = await client.post(registration_endpoint, content=body, headers=forward_headers)
557573

558-
# Forward the response as-is (OBP-OIDC returns correct token_endpoint_auth_method)
559-
return JSONResponse(
560-
response.json() if response.headers.get("content-type", "").startswith("application/json") else {"error": "registration_failed"},
561-
status_code=response.status_code,
562-
)
574+
if not response.headers.get("content-type", "").startswith("application/json"):
575+
return JSONResponse({"error": "registration_failed"}, status_code=response.status_code)
576+
577+
client_info = response.json()
578+
579+
# Strip null values — OBP-OIDC returns nulls for optional fields
580+
# (client_uri, logo_uri, contacts) that strict clients expect to be
581+
# absent rather than null
582+
client_info = {k: v for k, v in client_info.items() if v is not None}
583+
584+
return JSONResponse(client_info, status_code=response.status_code)
563585

564586
except Exception as e:
565587
logger.error(f"DCR forward error: {e}")
@@ -568,13 +590,23 @@ async def forward_register(request):
568590
status_code=500,
569591
)
570592

571-
# Add forwarding routes for clients that fetch from the resource server
593+
# Add forwarding routes at both paths:
594+
# - With mcp_path suffix: for RFC 8414 path-aware discovery from MCP endpoint URL
595+
# - Without suffix: for OASM discovery from authorization_servers URL (no path)
596+
suffix = mcp_path or ""
572597
routes.append(
573-
Route("/.well-known/oauth-authorization-server", endpoint=forward_oauth_authorization_server_metadata, methods=["GET"])
598+
Route(f"/.well-known/oauth-authorization-server{suffix}", endpoint=forward_oauth_authorization_server_metadata, methods=["GET"])
574599
)
575600
routes.append(
576-
Route("/.well-known/openid-configuration", endpoint=forward_openid_configuration, methods=["GET"])
601+
Route(f"/.well-known/openid-configuration{suffix}", endpoint=forward_openid_configuration, methods=["GET"])
577602
)
603+
if suffix:
604+
routes.append(
605+
Route("/.well-known/oauth-authorization-server", endpoint=forward_oauth_authorization_server_metadata, methods=["GET"])
606+
)
607+
routes.append(
608+
Route("/.well-known/openid-configuration", endpoint=forward_openid_configuration, methods=["GET"])
609+
)
578610
routes.append(
579611
Route("/register", endpoint=forward_register, methods=["POST"])
580612
)

0 commit comments

Comments
 (0)