How to Propagate OAuth2 Tokens Across Feign Calls in Spring Cloud

This article explains how to implement token propagation between microservices in a Spring Cloud OAuth2 environment by configuring a Feign interceptor, parsing relevant source code, handling token extraction and relay, and addressing common pitfalls such as missing tokens and thread isolation issues.

Java Architecture Diary
Java Architecture Diary
Java Architecture Diary
How to Propagate OAuth2 Tokens Across Feign Calls in Spring Cloud

Background Analysis

The client carries a token issued by the authentication center to request Resource Server A, the resource server validates the token, and Service A may need to call Service B via Feign or another RPC framework to assemble response data. This article focuses on the third step (A → B) and the custom token maintenance implementation.

Client carries token to request Resource Server A (Spring Security OAuth token source code analysis).

Resource server validates token and stores user context.

Service A calls Service B to assemble data.

How to Implement Token Transfer

Configure Feign Interceptor

Implement a Feign interceptor that extracts the token from the current OAuth2 client context and adds it to the outgoing request header.

@Bean
@ConditionalOnProperty("security.oauth2.client.client-id")
public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, OAuth2ProtectedResourceDetails resource) {
    return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, resource);
}

public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        template.header(header, extract(tokenType));
    }

    protected String extract(String tokenType) {
        OAuth2AccessToken accessToken = getToken();
        return String.format("%s %s", tokenType, accessToken.getValue());
    }

    public OAuth2AccessToken getToken() {
        OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken();
        if (accessToken == null || accessToken.isExpired()) {
            try {
                accessToken = acquireAccessToken();
            } catch (Exception e) {
                // handle exception
            }
        }
        return accessToken;
    }
}

Source Code Analysis

Retrieve token from the context and assemble it into the request header.

public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        template.header(header, extract(tokenType));
    }

    protected String extract(String tokenType) {
        OAuth2AccessToken accessToken = getToken();
        return String.format("%s %s", tokenType, accessToken.getValue());
    }

    public OAuth2AccessToken getToken() {
        OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken();
        if (accessToken == null || accessToken.isExpired()) {
            accessToken = acquireAccessToken();
        }
        return accessToken;
    }
}

AccessTokenContextRelay

A simple context relay that copies the token from the Spring Security context to the OAuth2 client context.

public class AccessTokenContextRelay {
    private OAuth2ClientContext context;

    public AccessTokenContextRelay(OAuth2ClientContext context) {
        this.context = context;
    }

    public boolean copyToken() {
        if (context.getAccessToken() == null) return false;
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
            OAuth2AuthenticationDetails holder = (OAuth2AuthenticationDetails) authentication.getDetails();
            String token = holder.getTokenValue();
            String tokenType = holder.getTokenType();
            DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(token);
            if (tokenType != null) {
                accessToken.setTokenType(tokenType);
            }
            context.setAccessToken(accessToken);
            return true;
        }
        return false;
    }
}

Common Issues

If the request context lacks a token, the Feign interceptor will fail because token copying fails.

Thread isolation can also cause token loss in child threads.

Using an interceptor for token forwarding improves performance but is limited to this specific scenario.

Custom Interceptor

Conditionally execute token relay based on custom headers.

public void apply(RequestTemplate template) {
    Collection<String> fromHeader = template.headers().get(SecurityConstants.FROM);
    if (CollUtil.isNotEmpty(fromHeader) && fromHeader.contains(SecurityConstants.FROM_IN)) {
        return;
    }
    accessTokenContextRelay.copyToken();
    if (oAuth2ClientContext != null && oAuth2ClientContext.getAccessToken() != null) {
        super.apply(template);
    }
}

Conclusion

The source code is based on a personal project using Spring Cloud and OAuth2.0 to build a Vue front‑back separated development platform.

Feel free to discuss Spring Cloud usage (QQ: 2270033969).

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

feignspring-cloudtoken-propagation
Java Architecture Diary
Written by

Java Architecture Diary

Committed to sharing original, high‑quality technical articles; no fluff or promotional content.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.