From 663e66af81d08caf9e959f26edf7b4b8aac3003d Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Mon, 29 Dec 2025 12:14:26 -0600 Subject: [PATCH] fix(oauth): Enable browser OAuth routes for Management API in hybrid mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The /oauth/login route was returning 404 in multi-user BasicAuth mode with offline access enabled. This was because browser OAuth routes were gated by `oauth_enabled` (only True for MCP OAuth modes), not by `oauth_provisioning_available` which correctly includes hybrid mode. The Management API (admin UI, webhook management) requires OAuth authentication regardless of how MCP tools authenticate. These are independent security concerns: - MCP Tools: BasicAuth (waiting for upstream Nextcloud OAuth patches) - Management API: OAuth (for admin UI, webhook management, vector sync) Changes: - Gate browser OAuth routes by oauth_provisioning_available instead of oauth_enabled - Add follow_redirects=True to OIDC discovery HTTP clients 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- nextcloud_mcp_server/app.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/nextcloud_mcp_server/app.py b/nextcloud_mcp_server/app.py index 8d33bff..4accbb1 100644 --- a/nextcloud_mcp_server/app.py +++ b/nextcloud_mcp_server/app.py @@ -690,7 +690,7 @@ async def setup_oauth_config(): logger.info(f"Performing OIDC discovery: {discovery_url}") # Perform OIDC discovery - async with httpx.AsyncClient() as client: + async with httpx.AsyncClient(follow_redirects=True) as client: response = await client.get(discovery_url) response.raise_for_status() discovery = response.json() @@ -994,7 +994,9 @@ async def setup_oauth_config_for_multi_user_basic( # Perform OIDC discovery try: - async with httpx.AsyncClient(timeout=30.0) as http_client: + async with httpx.AsyncClient( + timeout=30.0, follow_redirects=True + ) as http_client: response = await http_client.get(discovery_url) response.raise_for_status() discovery = response.json() @@ -2308,8 +2310,10 @@ def get_app(transport: str = "streamable-http", enabled_apps: list[str] | None = routes.append(Route("/oauth/authorize", oauth_authorize, methods=["GET"])) logger.info("OAuth login routes enabled: /oauth/authorize (Flow 1)") - # Add browser OAuth login routes (OAuth mode only) - if oauth_enabled: + # Add browser OAuth login routes for Management API access + # Available in OAuth modes AND multi-user BasicAuth with offline access + # (hybrid mode). Separate from MCP tool auth - Management API uses OAuth + if oauth_provisioning_available: from nextcloud_mcp_server.auth.browser_oauth_routes import ( oauth_login, oauth_login_callback,