From 5eb19858728f974e0f8399ef0e01ff5130a197db Mon Sep 17 00:00:00 2001 From: Ahmet Soormally Date: Fri, 6 Mar 2026 18:56:51 +0000 Subject: [PATCH] fix(mcp): deduplicate scopes in insufficient_scope challenge response The sendInsufficientScopeResponse function was aliasing operationScopes via challengeScopes := operationScopes, then appending to it. This caused operation scopes to appear twice in the WWW-Authenticate header when scopeChallengeIncludeTokenScopes was enabled. Build a fresh combined slice instead: existing token scopes first, then operation scopes (deduplicated). --- router/pkg/mcpserver/auth_middleware.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/router/pkg/mcpserver/auth_middleware.go b/router/pkg/mcpserver/auth_middleware.go index 1d12895c56..55c9adf3e0 100644 --- a/router/pkg/mcpserver/auth_middleware.go +++ b/router/pkg/mcpserver/auth_middleware.go @@ -219,18 +219,22 @@ func (m *MCPAuthMiddleware) sendInsufficientScopeResponse(w http.ResponseWriter, challengeScopes := operationScopes if m.scopeChallengeIncludeTokenScopes { - // Union of token's existing scopes + operation's required scopes + // Union of token's existing scopes + operation's required scopes. + // Existing scopes come first so the client retains them on re-auth. existing := extractScopes(claims) - seen := make(map[string]struct{}) + seen := make(map[string]struct{}, len(existing)+len(operationScopes)) + combined := make([]string, 0, len(existing)+len(operationScopes)) for _, s := range existing { seen[s] = struct{}{} - challengeScopes = append(challengeScopes, s) + combined = append(combined, s) } for _, s := range operationScopes { if _, ok := seen[s]; !ok { - challengeScopes = append(challengeScopes, s) + seen[s] = struct{}{} + combined = append(combined, s) } } + challengeScopes = combined } scopeList := strings.Join(challengeScopes, " ")