I am a complete noob with Spring Integration Framework.
I am trying to consume a REST API that uses OAuth2. I am using Spring Integration xml-based configuration.
My issue is that cannot seem to get the Gateway and the Rest Template wired properly to send the body (multi-part) for the token request
This is in my spring integration config file:
spring-integration-context.xml
<!-- Rest Template -->
<bean id="oAuth2RestTemplate"
class="org.springframework.security.oauth2.client.OAuth2RestTemplate">
<constructor-arg ref="clientCredentialsResource"/>
</bean>
<!-- Used by Rest Template -->
<bean id= "clientCredentialsResource"
class="org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails">
<property name="clientId" value="${oauth2.clientId}" />
<property name="clientSecret" value="${oauth2.clientSecret}" />
<property name="accessTokenUri" value="${oauth2.accessTokenUri}" />
</bean>
<!-- Channels for requesting token -->
<int:channel id="tokenRequestChannel"/>
<int:channel id="tokenResponseChannel"/>
<!-- Gateway for requesting token -->
<int-http:outbound-gateway id="authRequestGateway"
request-channel="tokenRequestChannel"
url="${oauth2.endPointUri}"
reply-timeout="30000"
http-method="GET"
rest-template="oAuth2RestTemplate"
reply-channel="tokenResponseChannel"
charset="UTF-8"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>
To obtain the initial token, I know I need to POST my credentials in the header similar to this:
Method: POST
Authorization: Basic <base64-encoded clientId:clientSecret>
Content-Type: application/x-www-form-urlencoded
And a adding multi-part (form) to the body
grant_type=client_credentials&scope=read
The API requires the string grant_type=client_credentials&scope=read
to be in the request’s body (not the URL) as this is a POST
(not a GET
).
I have tried a few things (too many to remember/recount all) but I am not sure where/how to put the payload into my request.
I am missing something (obvious?) and I don't know what it is at this point.
This is what I get in the logs (request/response):
2016-11-10 16:46:22.429 DEBUG 6384 --- [ask-scheduler-3] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@2d1f9cc810 pairs: {POST [redacted]/oauth/token HTTP/1.1: null}{Authorization: Basic [redacted]}{Accept: application/json, application/x-www-form-urlencoded}{Content-Type: application/x-www-form-urlencoded}{Cache-Control: no-cache}{Pragma: no-cache}{User-Agent: Java/1.8.0_72}{Host: [redacted]}{Connection: keep-alive}{Content-Length: 29} 2016-11-10 16:46:22.584 DEBUG 6384 --- [ask-scheduler-3] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@29200db310 pairs: {null: HTTP/1.1 400 Bad Request}{Cache-Control: no-cache}{Pragma: no-cache}{Content-Type: application/json; charset=utf-8}{Expires: -1}{Server: [redacted]}{[redacted]}{[redacted]}{Date: Fri, 11 Nov 2016 00:46:48 GMT}{Content-Length: 46} 2016-11-10 16:46:22.586 ERROR 6384 --- [ask-scheduler-3] o.s.integration.handler.LoggingHandler : org.springframework.messaging.MessageHandlingException: HTTP request execution failed for URI [redacted]; nested exception is error="access_denied", error_description="Access token denied." at org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:409) at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:109) at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127) at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116) at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148) at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121) at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77) at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423) at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:373) at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115) at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45) at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105) at org.springframework.integration.endpoint.SourcePollingChannelAdapter.handleMessage(SourcePollingChannelAdapter.java:195) at org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:272) at org.springframework.integration.endpoint.AbstractPollingEndpoint.access$000(AbstractPollingEndpoint.java:58) at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:190) at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:186) at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller$1.run(AbstractPollingEndpoint.java:353) at org.springframework.integration.util.ErrorHandlingTaskExecutor$1.run(ErrorHandlingTaskExecutor.java:55) at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:51) at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:344) at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: error="access_denied", error_description="Access token denied." at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:142) at org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider.obtainAccessToken(ClientCredentialsAccessTokenProvider.java:44) at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:142) at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:118) at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:615) at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:595) at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:516) at org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:382) ... 30 more Caused by: error="invalid_request", error_description="OAuth Error", message="{ "error": "invalid_scope" }" at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:120) at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:33) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3789) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2913) at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:225) at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readInternal(AbstractJackson2HttpMessageConverter.java:205) at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:193) at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport$AccessTokenErrorHandler.handleError(OAuth2AccessTokenSupport.java:235) at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:667) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:620) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:588) at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:137) ... 41 more
So, from what I can tell, I am getting an "Access Denied" message because I am unable to set the scope in the body of the request.
Note1
Also, I already looked at the code in the sample projects and did not see an answer to my issue.
Note2
I have verified (via POSTMAN) that the credentials, endpoints(URIs), scope, etc. are correct.
UPDATE 1
Based on comments from @Artem-Bilan, I changed the method to POST
and added the code below to include the body in the request.
<!-- Add Payload -->
<int:inbound-channel-adapter id="oauth2ChannelAdapter" channel="tokenRequestChannel"
ref="grantAndScope" method="getGrantTypeAndScope">
<!-- Triggering requests every 5 seconds -->
<int:poller fixed-delay="5000" />
</int:inbound-channel-adapter>
<!-- Bean with Grant & Scope Payload -->
<bean id="grantAndScope" class="com.AuthTypeAndScopeInfo"/>
AuthTypeAndScopeInfo.java
public class AuthTypeAndScopeInfo {
/* adds grant type and scope as body to message */
public MultiValueMap<String, String> getGrantTypeAndScope() {
// Create the request body as a MultiValueMap
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("grant_type", "client_credentials");
body.add("scope", "read");
return body;
}
}
The results, however, are the same :/