question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Feign Client don't reauthentificate on expired refresh token Oauth2

See original GitHub issue

Hi,

I struggle with 401 errors, when refresh token expired. I would like to code a workflow to handle 401 codes and force reauthentification Oauth2 on feign client. How can I do this? This is microservice on Spring Boot 2.0.5 and Spring Cloud Security

Feign client version: 2.1.3.RELEASE

    RequestInterceptor oauth2FeignRequestInterceptor() {
        return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(), resource());
    }

    private OAuth2ProtectedResourceDetails resource() {
        ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
        resourceDetails.setUsername(user);
        resourceDetails.setPassword(password);
        resourceDetails.setAccessTokenUri(accessTokenUri);
        resourceDetails.setClientId(clientId);
        resourceDetails.setClientSecret(clientSecret);
        resourceDetails.setGrantType("all");
        resourceDetails.setScope(Arrays.asList("all));
        return resourceDetails;
    }

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:5 (1 by maintainers)

github_iconTop GitHub Comments

9reactions
michmzrcommented, Oct 24, 2019

I have just finished a hard solution. It just reset access token, when gets 401 and retry request. I hope my snippets will help somebody with similiar problem

@Slf4j
public class CustomErrorDecoder implements ErrorDecoder {
    @Autowired
    FeignRequestInterceptor feignRequestInterceptor;

    @Override
    public Exception decode(String methodKey, Response response) {
        log.debug("Catched Feign client error {}, {}", methodKey, response);
        FeignException exception = errorStatus(methodKey, response);

        switch (response.status()) {
            case 401:
                log.debug("Clearing access token and retrying last reques ");
                feignRequestInterceptor.cleanAccessToken();
                return new RetryableException(exception.getMessage(), new Date());
            case 404:
                return new NotFoundException(response.reason());
            default:
                return new exception;
        }
    }
}
@Log4j
public class FeignRequestInterceptor extends OAuth2FeignRequestInterceptor {
    private OAuth2ClientContext clientContext;

    public FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, OAuth2ProtectedResourceDetails resource) {
        super(oAuth2ClientContext, resource);
        clientContext = oAuth2ClientContext;
    }

    @Override
    public OAuth2AccessToken getToken() {
        OAuth2AccessToken accessToken = clientContext.getAccessToken();

        if (accessToken == null || accessToken.isExpired()) {
            try {
                accessToken = acquireAccessToken();
            }catch (InvalidGrantException e) {
                log.error("Catched invalid grant exception: ", e);

                cleanAccessToken();
                accessToken = acquireAccessToken();
            }catch (HttpClientErrorException e) {
                log.error("Catched oauth http exception: ", e);

                if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) {
                    log.debug("Acquiring access token...");
                    cleanAccessToken();
                    accessToken = acquireAccessToken();
                } else {
                    throw e;
                }
            } catch (UserRedirectRequiredException e) {
                clientContext.setAccessToken(null);
                String stateKey = e.getStateKey();
                if (stateKey != null) {
                    Object stateToPreserve = e.getStateToPreserve();
                    if (stateToPreserve == null) {
                        stateToPreserve = "NONE";
                    }
                    clientContext.setPreservedState(stateKey, stateToPreserve);
                }
                throw e;
            }
        }
        return accessToken;
    }

    public void cleanAccessToken() {
        log.debug("Reset access token");

        clientContext.setAccessToken(null);
    }
}
@Configuration
public class FeignClientConfiguration {

    @Value("${oauth.accessTokenUri}")
    private String accessTokenUri;
    @Value("${oauth.clientId}")
    private String clientId;
    @Value("${oauth.clientSecret}")
    private String clientSecret;
    @Value("${oauth.client.scope}")
    private String scope;
    @Value("${oauth.client.user}")
    private String user;
    @Value("${oauth.client.password}")
    private String password;
    @Value("${oauth.grantType}")
    private String grantType = "password";

    @Bean
    FeignRequestInterceptor oauth2FeignRequestInterceptor() {
        return new FeignRequestInterceptor(new DefaultOAuth2ClientContext(), resource());
    }

    private OAuth2ProtectedResourceDetails resource() {
        ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
        resourceDetails.setUsername(user);
        resourceDetails.setPassword(password);
        resourceDetails.setAccessTokenUri(accessTokenUri);
        resourceDetails.setClientId(clientId);
        resourceDetails.setClientSecret(clientSecret);
        resourceDetails.setGrantType(grantType);
        resourceDetails.setScope(Arrays.asList(scope));
        return resourceDetails;
    }

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

    @Bean
    public ErrorDecoder errorDecoder() {
        return new CustomErrorDecoder();
    }

    @Bean
    public Retryer feignRetryer() {
        return new Retryer.Default(100, TimeUnit.SECONDS.toMillis(1), 2); // this will retry once
    }
}
0reactions
gbueno1291commented, Jan 10, 2022

@navaare Was any spring security configuration used for this?

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to provide an OAuth2 token to a Feign client using Spring ...
According documentation need use AuthorizedClientServiceOAuth2AuthorizedClientManager instead of DefaultOAuth2AuthorizedClientManager.
Read more >
OAuth2 for a Spring REST API – Handle the Refresh Token in ...
We learned how to store the Refresh Token in an AngularJS client app, how to refresh an expired Access Token and how to...
Read more >
What Are Refresh Tokens and How to Use Them Securely
This post will explore the concept of refresh tokens as defined by OAuth 2.0. We will learn how they compare to other token...
Read more >
spring-projects/spring-security-oauth - Gitter
Then using this authorization code we get the Access Token. But Access Token ... no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], ...
Read more >
Spring boot + Spring Security 5 + OAuth2/OIDC Client - Deep ...
Decoding magic behind spring boot oauth2 client. ... RestTemplate with a request interceptor that will refresh the tokens on expiry.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found