From e21ddd91b926a6221c51449ea8d76d816d6231d3 Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Tue, 7 Apr 2026 16:33:47 +0200 Subject: [PATCH] feat: add OIDC resource server scope prefix for Cognito compatibility When OIDC_RESOURCE_SERVER_ID is set, prefix resource scopes with the identifier when forwarding to the IdP (e.g., calendar.read becomes https://example.com/calendar.read). Required for IdPs like AWS Cognito that mandate {resource_server_id}/{scope} format for custom scopes. OIDC standard scopes (openid, profile, email) are forwarded as-is. Co-Authored-By: Claude Opus 4.6 (1M context) --- nextcloud_mcp_server/auth/oauth_routes.py | 16 +++++++++++++++- nextcloud_mcp_server/config.py | 2 ++ settings.toml | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/nextcloud_mcp_server/auth/oauth_routes.py b/nextcloud_mcp_server/auth/oauth_routes.py index f8839789..4c56cd23 100644 --- a/nextcloud_mcp_server/auth/oauth_routes.py +++ b/nextcloud_mcp_server/auth/oauth_routes.py @@ -345,12 +345,26 @@ async def oauth_authorize(request: Request) -> RedirectResponse | JSONResponse: f"Rewrote authorization endpoint for browser access: {authorization_endpoint}" ) + # Prefix resource scopes with the resource server identifier if configured. + # Required for IdPs like Cognito that use {identifier}/{scope} format. + # OIDC standard scopes are forwarded as-is. + oidc_scopes = {"openid", "profile", "email"} + resource_server_id = os.getenv("OIDC_RESOURCE_SERVER_ID", "") + if resource_server_id: + idp_scope_list = [ + f"{resource_server_id}/{s}" if s not in oidc_scopes else s + for s in scopes.split() + ] + idp_scope_str = " ".join(idp_scope_list) + else: + idp_scope_str = scopes + # Redirect to Nextcloud with MCP server's own client_id (no PKCE — confidential client) idp_params = { "client_id": mcp_server_client_id, "redirect_uri": callback_uri, "response_type": "code", - "scope": scopes, + "scope": idp_scope_str, "state": server_state, "prompt": "consent", "resource": f"{mcp_server_url}/mcp", # MCP server audience diff --git a/nextcloud_mcp_server/config.py b/nextcloud_mcp_server/config.py index dd2a3f6a..9114d018 100644 --- a/nextcloud_mcp_server/config.py +++ b/nextcloud_mcp_server/config.py @@ -213,6 +213,7 @@ class Settings: oidc_client_id: str | None = None oidc_client_secret: str | None = None oidc_issuer: str | None = None + oidc_resource_server_id: str | None = None # Nextcloud settings nextcloud_host: str | None = None @@ -568,6 +569,7 @@ def get_settings() -> Settings: "oidc_client_id": "NEXTCLOUD_OIDC_CLIENT_ID", "oidc_client_secret": "NEXTCLOUD_OIDC_CLIENT_SECRET", "oidc_issuer": "OIDC_ISSUER", + "oidc_resource_server_id": "OIDC_RESOURCE_SERVER_ID", # Nextcloud settings "nextcloud_host": "NEXTCLOUD_HOST", "nextcloud_username": "NEXTCLOUD_USERNAME", diff --git a/settings.toml b/settings.toml index b4cccac1..84f5f6db 100644 --- a/settings.toml +++ b/settings.toml @@ -37,6 +37,7 @@ oidc_issuer = "@none" jwks_uri = "@none" introspection_uri = "@none" userinfo_uri = "@none" +oidc_resource_server_id = "@none" # --- Mode flags --- enable_multi_user_basic_auth = false