2

I am using Refit (5.1.67) as my HttpClient wrapper, in a .NET Core 3.1 app using IHttpClientFactory.

The API I am calling is secured using a client credentials token.

I am registering the client with this:

services.AddRefitClient<ISomeApiClient>().ConfigureHttpClient(c =>
            c.BaseAddress = new Uri(Configuration["BaseUrlFromConfig"]));

The client has methods that look like this:

public interface ISomeApiClient
{
    [Get("/api/somewhere")]
    Task<IEnumerable<MyResponseObject>> GetItems([Header("X-User-Id")] string userId, [Header("Authorization")] string accessToken);

    [Get("/api/somewhere-else")]
    Task<MyResponseObject> GetItem([Header("X-User-Id")] string userId, [Header("Authorization")] string accessToken, int id);
}

What I want to avoid is having to explicitly pass accessToken and userId every time I call an endpoint (like above). Ideally, I want to have my client look like this:

public interface ISomeApiClient
{
    [Get("/api/somewhere")]
    Task<IEnumerable<MyResponseObject>> GetItems();

    [Get("/api/somewhere")]
    Task<IEnumerable<MyResponseObject>> GetItems(int id);
}

It feels like I need some sort of request middleware for outgoing requests, where I can add these two headers. If they were static I would just decorate the whole interface but because these are runtime values that will not work.

I cannot find any help on this one in the docs, and would appreciate any pointers.

Rob King
  • 1,131
  • 14
  • 20

2 Answers2

5

Refit docs now explain how to do this

https://github.com/reactiveui/refit#reducing-header-boilerplate-with-delegatinghandlers-authorization-headers-worked-example

Add a header handler:

class AuthHeaderHandler : DelegatingHandler
 {
    private readonly IAuthTokenStore authTokenStore;

    public AuthHeaderHandler(IAuthTokenStore authTokenStore)
    {
         this.authTokenStore = authTokenStore;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = await authTokenStore.GetToken();

        //potentially refresh token here if it has expired etc.

        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

        return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
    }
}

then register in Startup.cs when registering the client:

services.AddTransient<AuthHeaderHandler>();

services.AddRefitClient<ISomeThirdPartyApi>()
        .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"))
        .AddHttpMessageHandler<AuthHeaderHandler>();

Rob King
  • 1,131
  • 14
  • 20
  • Where does IAuthTokenStore come from? Is it something that you have to define? – benin101 Dec 19 '22 at 11:49
  • Yes, you can use anything here, its just a place to store your tokens. You could also directly call your auth provider for it, I cache mine as my auth provider has rate limiting – Rob King Jan 28 '23 at 00:58
0

You can use DI to inject your client where do you need it. We use it like this:

[ApiController]
public class ValuesController : ControllerBase
{
private readonly ISomeApiClient_client;

public ValuesController(ISomeApiClient client)
{
   _client = client;
   httpClient.DefaultRequestHeaders.Authorization =
                     new AuthenticationHeaderValue("Bearer", "Your Oauth token");
}

[HttpGet("/")]
public async Task<ActionResult<Reply>> Index()
{
  return await _client.GetMessageAsync();
}
}
Nicu Besliu
  • 453
  • 5
  • 17