Search code examples
javaspring-securitynetflix-feign

Forward request headers to multiple service calls in Spring + Netflix + Feign


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?


Solution

  • 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());                   
            }
        };
    }