diff --git a/nextcloud_mcp_server/auth/client_registry.py b/nextcloud_mcp_server/auth/client_registry.py index 85541cab..5a34bc8a 100644 --- a/nextcloud_mcp_server/auth/client_registry.py +++ b/nextcloud_mcp_server/auth/client_registry.py @@ -70,6 +70,24 @@ class ClientRegistry: ) logger.info(f"Registered static client: {client_id}") + # Load cloud clients (web-based, HTTPS redirect URIs) + # Format: "client_id|redirect_uri,client_id2|redirect_uri2" + cloud_clients = os.getenv("ALLOWED_MCP_CLOUD_CLIENTS", "").strip() + if cloud_clients: + for entry in cloud_clients.split(","): + entry = entry.strip() + if "|" in entry: + cid, redirect = entry.split("|", 1) + cid, redirect = cid.strip(), redirect.strip() + self._clients[cid] = MCPClientInfo( + client_id=cid, + name=self._get_client_name(cid), + redirect_uris=[redirect], + allowed_scopes=["*"], + is_public=True, + ) + logger.info(f"Registered cloud client: {cid}") + # Add well-known clients if not explicitly configured if not self._clients: self._add_well_known_clients() @@ -78,6 +96,7 @@ class ClientRegistry: """Get human-readable name for client_id.""" known_names = { "claude-desktop": "Claude Desktop", + "claude-ai": "Claude AI", "continue-dev": "Continue IDE Extension", "zed-editor": "Zed Editor", "vscode-mcp": "VS Code MCP Extension", diff --git a/nextcloud_mcp_server/auth/oauth_routes.py b/nextcloud_mcp_server/auth/oauth_routes.py index 63beff2b..f52fb789 100644 --- a/nextcloud_mcp_server/auth/oauth_routes.py +++ b/nextcloud_mcp_server/auth/oauth_routes.py @@ -1228,7 +1228,6 @@ async def oauth_register_proxy(request: Request) -> JSONResponse: ) oauth_config = oauth_ctx["config"] - nextcloud_host = oauth_config["nextcloud_host"] # Rate limit DCR requests per client IP client_ip = request.client.host if request.client else "unknown" @@ -1249,21 +1248,29 @@ async def oauth_register_proxy(request: Request) -> JSONResponse: timestamps.append(now) _dcr_rate_limit[client_ip] = timestamps - # Discover registration endpoint from OIDC discovery (prefer over hardcoded path) + # Discover registration endpoint from OIDC discovery discovery_url = oauth_config.get("discovery_url") + registration_endpoint = None if discovery_url: try: discovery = await _get_cached_discovery(discovery_url) - registration_endpoint = discovery.get( - "registration_endpoint", f"{nextcloud_host}/apps/oidc/register" - ) + registration_endpoint = discovery.get("registration_endpoint") except Exception: - logger.warning( - "Failed to fetch OIDC discovery for DCR endpoint, using fallback" - ) - registration_endpoint = f"{nextcloud_host}/apps/oidc/register" - else: - registration_endpoint = f"{nextcloud_host}/apps/oidc/register" + logger.warning("Failed to fetch OIDC discovery for DCR endpoint") + + if not registration_endpoint: + logger.warning( + "DCR proxy: Upstream IdP does not support dynamic client registration" + ) + return JSONResponse( + { + "error": "registration_not_supported", + "error_description": "The upstream identity provider does not support " + "dynamic client registration. Configure the client statically using " + "ALLOWED_MCP_CLIENTS or ALLOWED_MCP_CLOUD_CLIENTS.", + }, + status_code=400, + ) logger.info(f"DCR proxy: Forwarding registration to {registration_endpoint}")