Spring Cloud: Feign OAuth2 authentication

It’s worth to describe one additional use case for Spring Cloud Feign clients in microservice oriented architecture: authentication.

You may quite fast face the fact that your requests are being send across multiple services and that they may require to be aware of the user on behalf of whom the requests are being processed. Afterwards you can use that information to perform some user specific operations or simply perform authorization and verify if the user is permitted to perform specific actions.

Fortunately Spring Cloud Security module comes here with aid and whenever you use RestTemplate and OAuth2 authentication this information will be propagated with any remote call that you perform. The module will configure for you OAuth2RestTemplate that can be injected and used as normal RestOperations/RestTemplate. Providing that you had enable Spring’s OAuth2 context, which happens if you have enabled the resource server or enabled the OAuth2 client, using @EnableOAuth2Client.

Unfortunately this does not apply to your Feign clients, but we are going to change this through two simple steps. We are going to support OAuth Bearer token authentication.

First let’s define our custom RequestInterceptor.

public class OAuth2FeignRequestInterceptor implements RequestInterceptor {

    private static final String AUTHORIZATION_HEADER = "Authorization";

    private static final String BEARER_TOKEN_TYPE = "Bearer";

    private static final Logger LOGGER = LoggerFactory.getLogger(OAuth2FeignRequestInterceptor.class);

    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)) {
            LOGGER.warn("The Authorization token has been already set");
        } else if (oauth2ClientContext.getAccessTokenRequest().getExistingToken() == null) {
            LOGGER.warn("Can not obtain existing token for request, if it is a non secured request, ignore.");
        } else {
            LOGGER.debug("Constructing Header {} for Token {}", AUTHORIZATION_HEADER, BEARER_TOKEN_TYPE);
            template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE,
                    oauth2ClientContext.getAccessTokenRequest().getExistingToken().toString()));
        }
    }
}

We are going to use here Spring OAuth2 OAuth2ClientContext, that will store request bound OAuth token details.

Next step: we need to register that in our application, using Spring Boot we can leverage auto configuration features and register our class in META-INF/spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.jmnarloch.spring.cloud.feign.OAuth2FeignAutoConfiguration

Finally the configuration itself:

@Configuration
@ConditionalOnClass({ Feign.class })
@ConditionalOnProperty(value = "feign.oauth2.enabled", matchIfMissing = true)
public class OAuth2FeignAutoConfiguration {

    @Bean
    @ConditionalOnBean(OAuth2ClientContext.class)
    public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oauth2ClientContext) {
        return new OAuth2FeignRequestInterceptor(oauth2ClientContext);
    }
}

Basically that’s it. From now on every call that will be made will also go with Authorization header.

As always there is a Spring Cloud starter that you can use:

compile ('io.jmnarloch:feign-oauth2-spring-cloud-starter:1.0.0')

The source code is available at Github:

https://github.com/jmnarloch/feign-oauth2-spring-cloud-starter

2 comments

  1. Pingback: Spring Cloud: Eureka, Zuul and OAuth2 – scaling out authorization server | I just do things
  2. Daniel · August 11

    Hello, im trying to implement this solution, but i have a problem when the interceptor try to instance the oauth2ClientContext bean, and i dont know what im doing bad. Do you have any idea about this error?

    “Error creating bean with name ‘scopedTarget.oauth2ClientContext’: Scope ‘session’ 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”

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s