Loading src/main/java/org/etsi/osl/mcp/backend/configuration/McpConfiguration.java +40 −32 Original line number Diff line number Diff line package org.etsi.osl.mcp.backend.configuration; import org.springaicommunity.mcp.security.client.sync.AuthenticationMcpTransportContextProvider; 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.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 // @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()); // } /** * Returns the ID of the {@code spring.security.oauth2.client.registration}, if * unique. * 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. */ private static String findUniqueClientRegistration(ClientRegistrationRepository clientRegistrationRepository) { String registrationId; if (!(clientRegistrationRepository instanceof InMemoryClientRegistrationRepository repo)) { throw new IllegalStateException("Expected an InMemoryClientRegistrationRepository"); @Bean 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); } var iterator = repo.iterator(); var firstRegistration = iterator.next(); if (iterator.hasNext()) { throw new IllegalStateException("Expected a single Client Registration"); }; } registrationId = firstRegistration.getRegistrationId(); return registrationId; @Bean McpSyncClientCustomizer syncClientCustomizer() { return (name, syncSpec) -> syncSpec.transportContextProvider(new AuthenticationMcpTransportContextProvider()); } } No newline at end of file Loading
src/main/java/org/etsi/osl/mcp/backend/configuration/McpConfiguration.java +40 −32 Original line number Diff line number Diff line package org.etsi.osl.mcp.backend.configuration; import org.springaicommunity.mcp.security.client.sync.AuthenticationMcpTransportContextProvider; 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.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 // @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()); // } /** * Returns the ID of the {@code spring.security.oauth2.client.registration}, if * unique. * 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. */ private static String findUniqueClientRegistration(ClientRegistrationRepository clientRegistrationRepository) { String registrationId; if (!(clientRegistrationRepository instanceof InMemoryClientRegistrationRepository repo)) { throw new IllegalStateException("Expected an InMemoryClientRegistrationRepository"); @Bean 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); } var iterator = repo.iterator(); var firstRegistration = iterator.next(); if (iterator.hasNext()) { throw new IllegalStateException("Expected a single Client Registration"); }; } registrationId = firstRegistration.getRegistrationId(); return registrationId; @Bean McpSyncClientCustomizer syncClientCustomizer() { return (name, syncSpec) -> syncSpec.transportContextProvider(new AuthenticationMcpTransportContextProvider()); } } No newline at end of file