From 737d62fe917f161af3f957764cc32b79e3483fde Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Tue, 4 Nov 2025 03:26:13 +0100 Subject: [PATCH] fix: allow OAuth Bearer tokens on /mcp endpoint by excluding from session auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SessionAuthBackend was blocking MCP clients using OAuth Bearer tokens because it returned None when no session cookie was present, causing 401 responses before FastMCP's OAuth provider could validate Bearer tokens. Changes: - Add path-based exclusion to SessionAuthBackend.authenticate() - Skip session auth for paths using other authentication methods: - /mcp (FastMCP OAuth Bearer tokens) - /.well-known/oauth-protected-resource (public PRM endpoint) - /health/live, /health/ready (public health checks) - /oauth/login, /oauth/login-callback, /oauth/authorize (OAuth flow pages) - Browser routes (/user, /user/page, /oauth/logout) still require session cookies This allows MCP clients to connect with OAuth Bearer tokens while maintaining session-based authentication for browser UI routes. Testing: - OAuth tests pass (test_mcp_oauth_server_connection, etc.) - Browser routes still require session auth (/user returns 303 redirect) - Public endpoints remain accessible (/health/live works) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- nextcloud_mcp_server/auth/session_backend.py | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/nextcloud_mcp_server/auth/session_backend.py b/nextcloud_mcp_server/auth/session_backend.py index f702ee0..42a03d5 100644 --- a/nextcloud_mcp_server/auth/session_backend.py +++ b/nextcloud_mcp_server/auth/session_backend.py @@ -37,12 +37,33 @@ class SessionAuthBackend(AuthenticationBackend): ) -> tuple[AuthCredentials, SimpleUser] | None: """Authenticate the request based on session cookie or BasicAuth mode. + For paths that use other authentication mechanisms (OAuth Bearer tokens, + public endpoints), this backend returns None to skip session authentication + and allow those mechanisms to handle the request. + Args: conn: HTTP connection Returns: Tuple of (credentials, user) if authenticated, None otherwise """ + # Skip session auth for paths that use other authentication methods + # or are publicly accessible + excluded_paths = [ + "/mcp", # FastMCP OAuth Bearer tokens (handled by FastMCP's auth provider) + "/.well-known/oauth-protected-resource", # Public PRM metadata + "/health/live", # Health checks (public) + "/health/ready", + "/oauth/login", # Login flow (no auth required to access login page) + "/oauth/login-callback", # OAuth callback (receives code from IdP) + "/oauth/authorize", # Flow 1 authorize endpoint (no session required) + ] + + if any(conn.url.path.startswith(path) for path in excluded_paths): + # Don't interfere - let other auth mechanisms handle these paths + logger.debug(f"Skipping session auth for excluded path: {conn.url.path}") + return None + # BasicAuth mode: Always authenticated as the configured user if not self.oauth_enabled: username = os.getenv("NEXTCLOUD_USERNAME", "admin")