From dc7abcbd481a9c85b9bb1cc0cb233f2d1d6ffae9 Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Tue, 4 Nov 2025 06:09:16 +0100 Subject: [PATCH] fix: move audience mapper from scope to nextcloud-mcp-server client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The token-exchange-nextcloud scope was being inherited by DCR clients and requested by external MCP clients (like Gemini CLI), causing all tokens to have aud: "nextcloud" even when targeting the MCP server. ## Problem When external clients registered via DCR, they inherited all optional scopes from the realm defaults, including token-exchange-nextcloud. When these clients requested tokens, they would include this scope, which added aud: "nextcloud" via the scope's protocol mapper. This caused authentication failures for MCP server access: ``` 'aud': 'nextcloud' WARNING - Token rejected: wrong audience ['nextcloud'], expected nextcloud-mcp-server ``` ## Root Cause Client scopes with protocol mappers are applied whenever that scope is requested, regardless of which client requests it. The token-exchange-nextcloud scope was designed for the MCP server's own token requests to Nextcloud APIs, but external clients were also requesting it. ## Solution Move the audience mapper from the token-exchange-nextcloud scope to a direct protocol mapper on the nextcloud-mcp-server client itself. ### Changes 1. **Remove token-exchange-nextcloud from nextcloud-mcp-server optional scopes** - External DCR clients won't inherit this scope - Prevents external clients from requesting it 2. **Add nextcloud-audience protocol mapper directly to nextcloud-mcp-server** - Hardcode aud: "nextcloud" for this client only - Only tokens issued TO nextcloud-mcp-server will have this audience - External MCP clients won't be affected ## Behavior After Fix **Gemini CLI (DCR client) → MCP Server**: - Client doesn't have token-exchange-nextcloud scope - Token audience: Based on RFC 8707 resource parameter (if provided) - Result: No hardcoded audience ✅ **MCP Server (nextcloud-mcp-server) → Nextcloud APIs**: - Client has nextcloud-audience protocol mapper - Token audience: Always "nextcloud" (hardcoded) - Result: aud: "nextcloud" for Nextcloud API access ✅ ## Related - RFC 8707: Resource Indicators for OAuth 2.0 - Keycloak client scopes vs. client protocol mappers - DCR client scope inheritance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- keycloak/realm-export.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/keycloak/realm-export.json b/keycloak/realm-export.json index 270f30b..a28e29e 100644 --- a/keycloak/realm-export.json +++ b/keycloak/realm-export.json @@ -229,6 +229,18 @@ "fullScopeAllowed": true, "nodeReRegistrationTimeout": -1, "protocolMappers": [ + { + "name": "nextcloud-audience", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "nextcloud", + "access.token.claim": "true", + "id.token.claim": "false", + "introspection.token.claim": "true" + } + }, { "name": "sub", "protocol": "openid-connect", @@ -308,7 +320,6 @@ "phone", "offline_access", "microprofile-jwt", - "token-exchange-nextcloud", "notes:read", "notes:write", "calendar:read",