From 874e11b9e74f12666a23260b55e0eea6fee8eda0 Mon Sep 17 00:00:00 2001 From: denazi Date: Mon, 2 Feb 2026 13:09:01 +0200 Subject: [PATCH 1/2] Issue #7: Fix MCP BE Authorization issue with MCP server --- .../configuration/McpConfiguration.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/etsi/osl/mcp/backend/configuration/McpConfiguration.java b/src/main/java/org/etsi/osl/mcp/backend/configuration/McpConfiguration.java index 87ca06c..1737d6d 100644 --- a/src/main/java/org/etsi/osl/mcp/backend/configuration/McpConfiguration.java +++ b/src/main/java/org/etsi/osl/mcp/backend/configuration/McpConfiguration.java @@ -1,9 +1,16 @@ package org.etsi.osl.mcp.backend.configuration; +import org.springaicommunity.mcp.security.client.sync.AuthenticationMcpTransportContextProvider; +import org.springaicommunity.mcp.security.client.sync.oauth2.http.client.OAuth2AuthorizationCodeSyncHttpRequestCustomizer; +import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; +import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer; + /** * @author Daniel Garnier-Moiroux */ @@ -13,17 +20,17 @@ class McpConfiguration { // Disabled OAuth2 customization for MCP client - not needed for JWT-based API // The MCP server connection doesn't need OAuth2 authentication - // @Bean - // McpSyncHttpClientRequestCustomizer requestCustomizer(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager, - // ClientRegistrationRepository clientRegistrationRepository) { - // var registrationId = findUniqueClientRegistration(clientRegistrationRepository); - // return new OAuth2AuthorizationCodeSyncHttpRequestCustomizer(oAuth2AuthorizedClientManager, registrationId); - // } + @Bean + McpSyncHttpClientRequestCustomizer requestCustomizer(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager, + ClientRegistrationRepository clientRegistrationRepository) { + var registrationId = findUniqueClientRegistration(clientRegistrationRepository); + return new OAuth2AuthorizationCodeSyncHttpRequestCustomizer(oAuth2AuthorizedClientManager, registrationId); + } - // @Bean - // McpSyncClientCustomizer syncClientCustomizer() { - // return (name, syncSpec) -> syncSpec.transportContextProvider(new AuthenticationMcpTransportContextProvider()); - // } + @Bean + McpSyncClientCustomizer syncClientCustomizer() { + return (name, syncSpec) -> syncSpec.transportContextProvider(new AuthenticationMcpTransportContextProvider()); + } /** * Returns the ID of the {@code spring.security.oauth2.client.registration}, if -- GitLab From 127f46a0b90c889cc761f981a3c532f82c33e2af Mon Sep 17 00:00:00 2001 From: denazi Date: Mon, 2 Feb 2026 16:13:10 +0200 Subject: [PATCH 2/2] Issue #7: Fix MCP BE Auth header forwarding --- .../configuration/McpConfiguration.java | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/etsi/osl/mcp/backend/configuration/McpConfiguration.java b/src/main/java/org/etsi/osl/mcp/backend/configuration/McpConfiguration.java index 1737d6d..b0ce8ff 100644 --- a/src/main/java/org/etsi/osl/mcp/backend/configuration/McpConfiguration.java +++ b/src/main/java/org/etsi/osl/mcp/backend/configuration/McpConfiguration.java @@ -1,30 +1,49 @@ package org.etsi.osl.mcp.backend.configuration; import org.springaicommunity.mcp.security.client.sync.AuthenticationMcpTransportContextProvider; -import org.springaicommunity.mcp.security.client.sync.oauth2.http.client.OAuth2AuthorizationCodeSyncHttpRequestCustomizer; import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer; /** - * @author Daniel Garnier-Moiroux + * MCP Client Configuration for JWT Bearer Token Propagation + * + * Configures the MCP client to forward JWT Bearer tokens from incoming API requests + * to the MCP Server. This enables proper authorization when calling MCP Server tools + * that require authentication. + * + * @author OpenSlice Team */ @Configuration class McpConfiguration { - // Disabled OAuth2 customization for MCP client - not needed for JWT-based API - // The MCP server connection doesn't need OAuth2 authentication - + /** + * Configures JWT Bearer token propagation to MCP Server. + * + * When the MCP Backend receives an authenticated request with a JWT Bearer token + * (e.g., from Postman or other REST clients), this customizer extracts that token + * and forwards it to all MCP Server HTTP calls, ensuring proper authorization. + */ @Bean - McpSyncHttpClientRequestCustomizer requestCustomizer(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager, - ClientRegistrationRepository clientRegistrationRepository) { - var registrationId = findUniqueClientRegistration(clientRegistrationRepository); - return new OAuth2AuthorizationCodeSyncHttpRequestCustomizer(oAuth2AuthorizedClientManager, registrationId); + McpSyncHttpClientRequestCustomizer requestCustomizer() { + return (builder, connectionName, serverUri, endpoint, context) -> { + // Get the current authentication from SecurityContext + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + // Check if the authentication is JWT-based (from Bearer token) + if (authentication instanceof JwtAuthenticationToken jwtAuth) { + // Extract the JWT token value + String tokenValue = jwtAuth.getToken().getTokenValue(); + + // Add as Bearer token to the outgoing MCP Server request + builder.header("Authorization", "Bearer " + tokenValue); + } + }; } @Bean @@ -32,22 +51,4 @@ class McpConfiguration { return (name, syncSpec) -> syncSpec.transportContextProvider(new AuthenticationMcpTransportContextProvider()); } - /** - * Returns the ID of the {@code spring.security.oauth2.client.registration}, if - * unique. - */ - private static String findUniqueClientRegistration(ClientRegistrationRepository clientRegistrationRepository) { - String registrationId; - if (!(clientRegistrationRepository instanceof InMemoryClientRegistrationRepository repo)) { - throw new IllegalStateException("Expected an InMemoryClientRegistrationRepository"); - } - var iterator = repo.iterator(); - var firstRegistration = iterator.next(); - if (iterator.hasNext()) { - throw new IllegalStateException("Expected a single Client Registration"); - } - registrationId = firstRegistration.getRegistrationId(); - return registrationId; - } - } \ No newline at end of file -- GitLab