Search code examples
microservicesjhipsteroktaspring-cloud-feignfeign

Cannot use Feign client from a message consumer


I have a set of three microservices created with JHipster and configured with Okta OAuth2 autentication.

I've also added the integration with RabbitMQ message service so one of the service produce messages on certain events and other one consume those messages to update its database.

Now the second service, to full update its data, needs information from a third service that I would like to call with FeignClient but it fails with the following stack:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:362)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:193)
at com.sun.proxy.$Proxy237.getAccessToken(Unknown Source)
at it.myefm.myspot.people.security.oauth2.AuthorizationHeaderUtil.getAuthorizationHeaderFromOAuth2Context(AuthorizationHeaderUtil.java:26)
at it.myefm.myspot.people.client.TokenRelayRequestInterceptor.apply(TokenRelayRequestInterceptor.java:23)
at feign.SynchronousMethodHandler.targetRequest(SynchronousMethodHandler.java:158)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:88)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46)
... 113 more

Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:42)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:350)
... 126 more

I think the problem is that the Feign execution does not start from a web request so there is no authentication infos in context...right?

Is there other way to reach my data between services?


Solution

  • The problem is your TokenRelayRequestInterceptor that it tries to resolve authentication information from the current thread-bound security context.

    Obviously, when you are in a consumer thread, you don't have such information (by default at least) thus the resolution fails.

    The thing you can do is the following:

    • Pass the necessary information in the event (might be problematic for Event sourced systems if it's the access token as it might expire)
    • Somehow try to fake the access token which is accepted by the third service

    On the consumer side, you can either manually set up the SecurityContext with the information needed for the interceptor or you forget about the interceptor on the consumer side and manually provide the data the third service needs (I guess it's just an Authorization header).

    UPDATE You can also create an internal endpoint in the third service which doesn't require any authentication at all, it's not accessible by externals.