0

I was finally able to protect a rest api with Okta as OAuth2.0 security provider (basic with defaults). Also able to get the bearer token using curl, to call rest api via postman and get back results.

curl --location --request POST 'https://dev-XXXXXX.okta.com/oauth2/default/v1/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=XXXXXXXXXXXXX' \
--data-urlencode 'client_secret=XXXXXXXXXXXXXXXXXXXXXXXXXXX' \
--data-urlencode 'grant_type=client_credentials

Now i am trying to implement FeignClient (Rest client) in Spring Boot 2.X app to call the protected api, but facing difficulty in finding right documentation/samples as guide. Appreciate any directions/suggestions?

Arpit S
  • 137
  • 2
  • 10

1 Answers1

1

To collect the token with Feign you need the following:

import java.util.Map;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.feign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;

@FeignClient(name = "oauth2", url = "https://dev-XXXXXX.okta.com/oauth2/default/v1")
public interface TokenFetcher {

    @PostMapping(value = "/token", consumes = APPLICATION_FORM_URLENCODED_VALUE)
    String token(@RequestBody Map<String, ?> form);

    class Configuration {
        @Bean
        Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> converters) {
            return new SpringFormEncoder(new SpringEncoder(converters));
        }
    }
}

Use the client like this:

@Autowired
TokenFetcher tokenFetcher;

public void test() {
    Map<String, Object> form = new HashMap<>();
    form.put("client_id", "xxxxxx");
    form.put("client_secret", "xxxxxx");
    form.put("grant_type", "client_credentials");
    String jwt = tokenFetcher.token(form);
}

Dependencies are:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>io.github.openfeign.form</groupId>
    <artifactId>feign-form-spring</artifactId>
    <version>3.8.0</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
</dependency>

In order to use the token you must to add it token as the 'authorization' header with a prefix of 'Bearer ' (note the space) on each call. The easiest way to do this is be adding a RequestInterceptor to your FeignClient as follows:

public class FeignConfiguration {

@Bean
public RequestInterceptor requestInterceptor() {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        String jwtTokenStoredSomewhere = null;
        if (requestTemplate.request().url().startsWith("https://site1")) {
            jwtTokenStoredSomewhere = site1JwtTokenStoredSomewhere;
        } else if (requestTemplate.request().url().startsWith("https://site2")) {
            jwtTokenStoredSomewhere = site2JwtTokenStoredSomewhere;
        } else {
            jwtTokenStoredSomewhere = site3JwtTokenStoredSomewhere;
        }
        requestTemplate.header("Authorization", "Bearer " + jwtTokenStoredSomewhere);
    }
}

}

John Williams
  • 4,252
  • 2
  • 9
  • 18
  • Thanks for suggestion. Actually I was more trying to look for guidance for getting the token itself. I was able to do so using below config (with OAuth Feign Config etc and keycloack). Now trying to do with okta spring.security.oauth2.client.registration.keycloak.authorization-grant-type=client_credentials spring.security.oauth2.client.registration.keycloak.client-id=xyx-app spring.security.oauth2.client.registration.keycloak.client-secret=XXXXX spring.security.oauth2.client.provider.keycloak.token-uri=http://localhost:8083/auth/realms/xxxxxxx/protocol/openid-connect/token – Arpit S Jan 29 '23 at 11:37
  • I have updated my answer to show how to collect the token in a general way (ie not limited to okta or keycloak) – John Williams Jan 29 '23 at 12:26
  • Thanks! John once again. I was able to successfully call the resource with token using interceptor set to feign client (slightly different implementation). But now trying to extend functionality to support https://stackoverflow.com/questions/75290401/using-single-feign-client-for-multiple-endpints or https://stackoverflow.com/questions/75242673/use-single-rest-client-feign-regular-rest-template-to-conditionally-call-diffe . Appreciate any help – Arpit S Jan 30 '23 at 21:39
  • If my answer worked, please accept it. If you get stuck implementing multiple feign targets then ask a specific question. I will take a look. – John Williams Jan 31 '23 at 09:47
  • i was able to make it work using https://www.baeldung.com/spring-cloud-feign-oauth-token, but for a single endpoint with one feign client. In meantime, i am stuck on how to call(POST to) different external REST endpoint, with their own respective security (one endpoint is secured via OAuth2, another is secured by basic auth, third is secured again via OAuth2[different cliendid/secret/okta account] and so on...) using single feign client, with request payload same – Arpit S Feb 24 '23 at 06:07
  • I have updated my answer to cover different endpoints being called – John Williams Feb 26 '23 at 10:12