I have some problems with the configuration of the Client Credentials flow in my Client app (My Spring Gateway).
My Authorization server is functional and tests with Postman without any problem.
But in my Client application, it seems that the oauth2 configuration is broken without error of compilation.
When I call a protected resource on my server resource, my client application seems to attempt to call an URL in its base and not the authorization server.
See the code My configuration file:
security:
oauth2:
client:
registration:
apigateway:
provider: apigateway
client-id: apigateway
client-secret: password
scope: generate_token,read,write
authorization-grant-type: client_credentials
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
provider:
apigateway:
token-uri: https://localhost:9001/oauth/token
My Client dependency:
<dependencies>
<!-- ************ SPRING DEPENDENCIES ************ -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
</dependencies>
Configuration of my WebClient:
public class WebClientSecurityCustomizer implements WebClientCustomizer {
private ServerOAuth2AuthorizedClientExchangeFilterFunction securityExchangeFilterFunction;
public WebClientSecurityCustomizer(
ServerOAuth2AuthorizedClientExchangeFilterFunction securityExchangeFilterFunction) {
this.securityExchangeFilterFunction = securityExchangeFilterFunction;
}
@Override
public void customize(WebClient.Builder webClientBuilder) {
SslProvider sslProvider = SslProvider.builder().sslContext(
SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE)
)
.defaultConfiguration(SslProvider.DefaultConfigurationType.NONE).build();
TcpClient tcpClient = TcpClient.create().secure(sslProvider);
HttpClient httpClient = HttpClient.from(tcpClient);
ClientHttpConnector httpConnector = new ReactorClientHttpConnector(httpClient);
webClientBuilder.clientConnector(httpConnector);
webClientBuilder.filters((filterFunctions) -> {
if (!filterFunctions.contains(this.securityExchangeFilterFunction)) {
filterFunctions.add(0, this.securityExchangeFilterFunction);
}
});
}
}
@Configuration
public class WebClientSecurityConfiguration {
@Bean
public WebClientSecurityCustomizer webClientSecurityCustomizer(
ReactiveClientRegistrationRepository clientRegistrations) {
// Provides support for an unauthenticated user such as an application
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrations, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
// Build up a new WebClientCustomizer implementation to inject the oauth filter
// function into the WebClient.Builder instance
return new WebClientSecurityCustomizer(oauth);
}
/**
* Helper function to include the Spring CLIENT_REGISTRATION_ID_ATTR_NAME in a
* properties Map
*
* @param provider - OAuth2 authorization provider name
* @return consumer properties Map
*/
public static Consumer<Map<String, Object>> getExchangeFilterWith(String provider) {
return ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId(provider);
}
}
My caller on the resource:
return webClientBuilder.build().get().uri(uri+"{accessToken}", accessToken)
.attributes(
WebClientSecurityConfiguration.getExchangeFilterWith("apigateway"))
.retrieve()
.bodyToMono(String.class)
.flatMap(response -> {
ServerHttpRequest request = exchange.getRequest().mutate()
.header(jwtHeader, String.format("%s %s", jwtPrefix, response))
.build();
return chain.filter(exchange.mutate().request(request).build());
});
}
And to finish, the error generated in the client application (it seems that Authorization and resource serve don't receive request):
2019-12-02 13:53:50.543 ERROR 11492 --- [ctor-http-nio-2] a.w.r.e.AbstractErrorWebExceptionHandler : [405f3f3c] 500 Server Error for HTTP GET "/oauth2/authorization/apigateway"
java.lang.IllegalArgumentException: Invalid Authorization Grant Type (client_credentials) for Client Registration with Id: apigateway
at org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver.authorizationRequest(DefaultServerOAuth2AuthorizationRequestResolver.java:156) ~[spring-security-oauth2-client-5.2.1.RELEASE.jar:5.2.1.RELEASE]
Thanks a lot for your help.