'Spring oauth2 webclient ClientAuthorizationException: [server_error]
I am trying to utilize Spring 5 Webclient and the built in oauth2 security features.
@Bean("oauth2WebClient")
public WebClient oauth2WebClient(final ReactiveOAuth2AuthorizedClientManager reactiveOAuth2AuthorizedClientManager) {
final ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(reactiveOAuth2AuthorizedClientManager);
serverOAuth2AuthorizedClientExchangeFilterFunction.setDefaultClientRegistrationId("app");
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(new ClassPathResource("app.jks").getFile()), "password".toCharArray());
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(new FileInputStream(new ClassPathResource("app.jks").getFile()), "password".toCharArray());
// Set up key manager factory to use our key store
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "password".toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
SslContext sslContext = SslContextBuilder.forClient()
.keyManager(keyManagerFactory)
.trustManager(trustManagerFactory)
.build();
//
//
// List<Certificate> certificateList = Collections.list(trustStore.aliases())
// .stream()
// .filter(t -> {
// try {
// return trustStore.isCertificateEntry(t);
// } catch (KeyStoreException e1) {
// throw new RuntimeException("Error reading truststore", e1);
// }
// })
// .map(t -> {
// try {
// return trustStore.getCertificate(t);
// } catch (KeyStoreException e2) {
// throw new RuntimeException("Error reading truststore", e2);
// }
// })
// .collect(Collectors.toList());
//
// X509Certificate[] certificates = certificateList.toArray(new X509Certificate[certificateList.size()]);
// //PrivateKey trustedCerts = (PrivateKey) keyStore.getKey("certs", "password".toCharArray());
// //PrivateKey privateKey = (PrivateKey) keyStore.getKey("certs", "password".toCharArray());
// //Certificate[] certChain = keyStore.getCertificateChain("certs");
//// X509Certificate[] x509CertificateChain = Arrays.stream(certChain)
//// .map(certificate -> (X509Certificate) certificate)
//// .collect(Collectors.toList())
//// .toArray(new X509Certificate[certChain.length]);
//
// SslContext sslContext = SslContextBuilder.forClient()
// .keyManager(keyManagerFactory)
// .trustManager(certificates)
// .build();
HttpClient httpClient = HttpClient.create()
.secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
return WebClient.builder()
.filter(serverOAuth2AuthorizedClientExchangeFilterFunction)
.clientConnector(connector)
.build();
I have confirmed the SSL certs are setup correctly and work when I use the Webclient to get the access token and fire off the service calls using that token.
When I use the built in oauth2 libs, I get the following error:
org.springframework.security.oauth2.client.ClientAuthorizationException: [server_error]
I see the below in the stack trace
reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Request to POST <url>[DefaultWebClient]
Stack trace:
java.lang.Exception: #block terminated with an error
What am I doing wrong here?
Solution 1:[1]
In addition to checking the SSL certificates as recommended in the comments of the original post, try double checking the server's response in a debugger.
I was able to see the HTTP response's status code in org.springframework.security.oauth2.core.web.reactive.function.OAuth2AccessTokenResponseBodyExtractor::extract (see inputMessage.response.responseState.response.status)
I was able to see the JSON response body by setting a breakpoint in OAuth2AccessTokenResponseBodyExtractor::parse method.
In my case the problem was obvious. A proxy returned a non-OAuth error message.
To capture the error in my code, I used WebClientReactiveClientCredentialsTokenResponseClient::setWebClient and WebClient.builder().filter() to log the body using this filter:
ExchangeFilterFunction logError = ExchangeFilterFunction.ofResponseProcessor(
clientResponse -> {
if (clientResponse.statusCode() != null && !clientResponse.statusCode().isError()) return Mono.just(clientResponse);
LOGGER.error("OAuth response error: {}", clientResponse.statusCode().toString());
return clientResponse
.bodyToMono(String.class)
.doOnNext(s -> {
LOGGER.error("OAuth response body: {}", s);
LOGGER.error("Note: OAuth response body has been consumed for logging. Spring OAuth client library will report that the response is empty.");
})
.map(s -> clientResponse);
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
