I have a bunch of intermediate and core services within my application. All services are Spring Boot and using Netflix Library. When a user requests information, the request will/might pass other services in the chain eg:
Client <-> Zuul <-> Service B <-> Service A
I have configured all services (A and B) to be ResourceServer so that every access needs to be authenticated. When requesting an access token (From a Spring Security Server) and use it to request information directly from Service A, everything works fine. When I use the same token to access information from Service B (which needs Service A down the line) I get an "HTTP 401: Full authentification is required" error. Service B uses a FeignClient to call Service A.
After some debugging, I found out, that the Authorization-Header is not passed from Service B to Service A. Service B checks the token itself correctly, grants access to the method and tries to perform the request of Service A.
I tried a RequestInterceptor but without any success (Error "Scope 'request' is not active for the current thread")
@Component
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String BEARER_TOKEN_TYPE = "Bearer";
private final OAuth2ClientContext oauth2ClientContext;
public OAuth2FeignRequestInterceptor(OAuth2ClientContext oauth2ClientContext) {
Assert.notNull(oauth2ClientContext, "Context can not be null");
this.oauth2ClientContext = oauth2ClientContext;
}
@Override
public void apply(RequestTemplate template) {
if (template.headers().containsKey(AUTHORIZATION_HEADER)) {
...
} else if (oauth2ClientContext.getAccessTokenRequest().getExistingToken() == null) {
...
} else {
template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE,
oauth2ClientContext.getAccessTokenRequest().getExistingToken().toString()));
}
}
}
This is an example proxy function that uses the FeignClient:
@Autowired
private CategoryClient cat;
@HystrixCommand(fallbackMethod = "getAllFallback", commandProperties = {@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2") })
@GetMapping("/category")
public ResponseEntity<List<Category>> getAll() {
try {
ResponseEntity<List<Category>> categories = this.cat.getAll();
...
return categories;
} catch(Exception e) {
...
}
}
Is there any working solution to pass the Authorization-Header from the proxy function to the FeignClient so that Service A will receive the header and can do its own auth check with it?
Found a working solution. I still don't know if this is the "best" way to do it and if anyone got a better solution I'd be happy if you share it. But for now, this is working as expected:
@Bean
public RequestInterceptor requestTokenBearerInterceptor() {
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
requestTemplate.header("Authorization", "Bearer " + details.getTokenValue());
}
};
}