-1

Hello I'm new to Java and Springboot. I'm currently working with an API where before making a POST request, I would need to generate a Bearer token. In order to generate a Bearer token, I would need to pass in my basic auth credentials to the "/oauth/token" endpoint. My application is having trouble passing my basic auth credentials since by the time I hit the "/v1/some-endpoint", I'm denied authorization because the Bearer token is null.

Here's my initial solution thinking I could check the url in the interceptor, then executing the following line but after debugging, it doesn't seem to be hitting that line.

Is there something I'm missing or not implementing correctly? Am I not implementing the Basic Auth endpoint correctly? Let me know if you need more information. Thanks

@Profile("!offline")
@FeignClient(
value = "someClient",
url = "${someProperty.url}",
configuration = SomeClient.SomeClientConfig.class)
public interface someClient {

  @PostMapping("/v1/some-endpoint")
  void redeemSomething(someRequestBody data);

  @PostMapping("/oauth/token")
  static BasicAuthResponse getBasicAuthToken() {
   return new BasicAuthResponse();
 }

@AllArgsConstructor
class SomeClientConfig extends BaseClientConfig {

private final SomeProperties properties;


private final SomeAuthTokenSupplier tokenSupplier = new SomeAuthTokenSupplier();

@Bean
@Override
public CloseableHttpClient apacheClient() {
  return apacheClientFactory(properties.getUseProxy());
}

@Bean
public RequestInterceptor someAuthInterceptor() {

  return template -> {

    if(template.url().equals("/oauth/token")) {
      String authToken = Base64Utils.encodeToString((properties.getCredentials().getUser() + ":" + properties.getCredentials().getUser()).getBytes(Charset.forName("UTF-8")));
      template.header("Authorization", authToken);
    }

    template.header("Authorization", String.format("Bearer %s", tokenSupplier.getToken()));

  };
}

private class SomeAuthTokenSupplier {
  private volatile String token;
  private volatile long retrievedOn = -1L;

  String getToken() {

    if (updateTokenRequired()) {

      synchronized (this) {
        if (updateTokenRequired()) {

          BasicAuthResponse tokenResponse = getBasicAuthToken();
          token = tokenResponse.getAccess_token(); // new token from some api should be assigned here
          retrievedOn = Instant.now().toEpochMilli();
        }
      }
    }

    return token;
  }

  private boolean updateTokenRequired() {
    return  token == null || LocalDateTime.now().minusHours(8L).isAfter(LocalDateTime.ofInstant(Instant.ofEpochMilli(retrievedOn), ZoneId.systemDefault()));
  }

}

@Override
public Retryer retryer() {
  return new ClientRetry(250L, 2, 3) {
    @Override
    public void continueOrPropagate(RetryableException e) {
      if (e.status() == 401 || e.status() == 403) {
        tokenSupplier.token = null;
      }
      super.continueOrPropagate(e);
    }

    };
   }
  }
 }
Sophie
  • 103
  • 6

1 Answers1

0

It worth using standard Spring Security OAuth2 Client feature instead in order to support authorization in Feign clients

See docs and code samples: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2client

UPD

See another code sample: https://github.com/int128/feign-oauth2-example

If several service endpoints require different authentication, then it's worth having several Feign clients, each with own configuration

Mikhail Kopylov
  • 2,008
  • 5
  • 27
  • 58