Trey Bernstien
Trey Bernstien

Reputation: 153

How to send Bearer authorization token using Spring Boot and @FeignClient

I am using Spring Boot to write an application that interacts with HTTP rest servers. One of the servers I'm connecting to (Wit.ai) uses a beaerer authorization token. A curl request that yields a successful response looks like this:

GET /message?q=sample message HTTP/1.1
Host: api.wit.ai
Authorization: Bearer XXXXXXXXXXXXX
Cache-Control: no-cache
Postman-Token: 526c3a11-8e61-4552-aa19-e913f6473753

The wit.ai docs say the following about the token,

Wit.ai uses OAuth2 as an authorization layer. As such, every API request must contain an Authorize HTTP header with a token Access tokens are app specific.

I am trying to send a GET request to this endpoint in a Spring Boot app using @FeignClient. However I the endpoint doesn't seem to be accepting my authorization token. Here is my FeignClient code

@FeignClient(name="witGetter", url = "${wit.url}")
    public interface WitGetter {
        @RequestMapping(method = RequestMethod.GET, value = "/message?v=20180507q={text}",
            headers = {"Authorization: Bearer XXXXXXXXXXXXX"})
        WitResponse getWitResponse(@PathVariable("text") final String text);
}

What is the proper way to pass such an authorization token? I have tried a few other things but to no avail. Thanks for any advice!!!

By the way, the following code works using a traditional Feign interface, but I need to use @FeignClient in this case.

public interface WitGetter {
    @Headers("Authorization: Bearer XXXXXXXXXXXXX")
    @RequestLine("GET /message?q={text}")
    WitResponse getWitResponse(@Param("text") String text);
}

(code below is in a separate config file)

@Bean
    public WitGetter defaultWitGetter(@Value("https://api.wit.ai") final String witUrl){
        return Feign.builder().decoder(new GsonDecoder()).target(WitGetter.class, witUrl);

}

EDIT

The error code I get when using the above code is:

Exception in thread "main" feign.FeignException: status 400 reading WitGetter#getWitResponse(String,String); content: { "error" : "Bad auth, check token/params", "code" : "no-auth" }

Upvotes: 15

Views: 50685

Answers (3)

Entrusc
Entrusc

Reputation: 357

You can of course annotate the method with a Header annotation and have an extra token parameter for every call your client provides, but that is not really an elegant solution as the caller needs to have access to the API key.

Actually the easiest and most straight forward solution is to create a configuration that is used by your FeignClient like so:

@Configuration
public class WitGetterConfiguration {

    private final String apiKey;

    public WitGetterConfiguration(@Value("${wit.api-key}") String apiKey) {
        this.apiKey = apiKey;
    }

    @Bean
    public ApiKeyInterceptor interceptor() {
        return new ApiKeyInterceptor(apiKey);
    }
}

The corresponding ApiKeyInterceptor is easy to implement and looks like this:

public class ApiKeyInterceptor implements RequestInterceptor {
    private final String apiKey;

    public ApiKeyInterceptor(String apiKey) {
        this.apiKey = apiKey;
    }

    @Override
    public void apply(RequestTemplate template) {
        template.header("Authorization", "Bearer " + apiKey);
    }
}

Finally you can then set your FeignClient to use the configuration in your client:

@FeignClient(name="witGetter", url = "${wit.url}", configuration = WitGetterConfiguration.class)
public interface WitGetter {
    
    @RequestMapping(method = RequestMethod.GET, value = "/message?v=20180507q={text}")
    WitResponse getWitResponse(@PathVariable("text") final String text);
    
}

This way the API key is sent as part of the header in every request that your client issues.

Upvotes: 4

Ait Friha Zaid
Ait Friha Zaid

Reputation: 1522

package com.example.demo.web;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.entities.CustomerDto;
import com.example.demo.feign.CustomerRestClient;

import jakarta.servlet.http.HttpServletRequest;

@RestController
@RequestMapping("customers")
@CrossOrigin("*")
public class ClientRestController {
    @Autowired
    private CustomerRestClient customerRestClient;

    @GetMapping("/all")
    public List<CustomerDto> getAllCustomers(HttpServletRequest request) {
        String authorizationHeader = request.getHeader("Authorization");
        return customerRestClient.listCustomes(authorizationHeader);
    }
}

import java.util.List;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;

import com.example.demo.entities.CustomerDto;

import feign.Headers;



@FeignClient(url = "http://localhost:8085/customers",value ="customer-rest-client")
@Headers("Authorization: {token}")
public interface CustomerRestClient {
    @GetMapping("/all")
    public List<CustomerDto> listCustomes(@RequestHeader("Authorization") String token);
}

enter image description here

Upvotes: 2

Arnold Galovics
Arnold Galovics

Reputation: 3416

When using Feign via Spring Cloud, you can use it as you would define a standard Spring MVC controller.

Please check my article here about passing headers with Feign: https://arnoldgalovics.com/passing-headers-with-spring-cloud-feign/

Quick hint: you can add a @RequestHeader("Authorization") String bearerToken parameter to the method definition.

And then of course call it like client.method(..., "Bearer " + token)

Upvotes: 30

Related Questions