4

I'm using a C# Refit client to have my services talk to one another via http.

I'm trying to send a Bearer token through the Authorization header, but according to the error message, it's not setting the AZ header in the request (see bottom). I've tried setting it through providing all headers, and using the [Authorize] attribute, and all the other methods they describe in their readme.

Here is my Refit client api call definition:


[Post(PresentationsBasePath + "/{presentationId}/cart")]
Task AddItemToCartAsync(long presentationId, ShoppingCartItemView item, [HeaderCollection] IDictionary<string, string> headers);

//calling it here:

await _api.AddItemToCartAsync(presentationId, item, GetTokenHeader(presentationId, token));

private Dictionary<string, string> GetTokenHeader(long presentationId, string token) => new()
{
    ["pres_id"] = presentationId.ToString(),
    [HeaderNames.Authorization] = $"Bearer {token}",
};

However, I'm getting a 401, and looking at the Refit.ApiException that's thrown, the RequestMessage.Headers does not contain the Authorization header.

Here's how I'm registering my refit api IPresentationsApi. I'm not doing anything relating to auth in the DI configuration


var refitSettings = GetRefitSettings();

void Configure<T>() where T : class => services
    .AddRefitClient<T>()
    .ConfigureHttpClient(ConfigureHttpClient);

Configure<IMarsPresentationApi>();
//other apis configured below

    
    private static void ConfigureHttpClient(IServiceProvider sp, HttpClient client)
    {
        var config = sp.GetRequiredService<IMarsConfiguration>();
        if (config.BaseUrl == null)
            throw new InvalidOperationException("Mars:BaseUrl must be configured");
        client.BaseAddress = new Uri(config.BaseUrl);
    }

Error shown here- you can see I get 401, and AZ header is not set in the request:

Error

What am I doing wrong? How do I get it to send the AZ header?

DLeh
  • 23,806
  • 16
  • 84
  • 128
  • If you are at the client sending a request you should be using PUT methods (not GET). – jdweng May 09 '22 at 14:19
  • i'm not doing a GET, i'm doing a POST. but that wouldn't matter, AZ is acceptable on all http methods. – DLeh May 09 '22 at 14:48
  • I don't understand the code in the method : GetRefitSettings. You should be deserializing if you are parsing the response. – jdweng May 09 '22 at 15:36
  • @jdweng i'm not parsing the response. I'm using refit as normal. I included the GetRefitSettings() for completion but it shouldn't be relevant to this problem at all. – DLeh May 10 '22 at 13:10
  • First, every server is different. What parameters are needed for one server may be different for a second server of application. You are getting a 401 status in the response because the request doesn't meet the server/app requirements. The parameters are either in the URL, and/or the HTTP headers. The request is sending data and the request should be using Put methods, not Get methods. HTTP the client sends a request, the server does processing, and then returns a response. Do not get the request code mixed up with the response code. The request is done before your get a response. – jdweng May 10 '22 at 13:25
  • @jdweng none of that matters. if i'm telling my code to set the bearer token in a header, and it's not setting it, it doesn't matter what the other server is doing. These are both my services and I know that it's 401ing because it's not getting set. – DLeh May 11 '22 at 15:46
  • If you do not send the authorization then you will fail. Your code is NOT putting data authorization into the request. It is reading the headers from the request which is backwards. – jdweng May 11 '22 at 17:39
  • @jdweng that is exactly my question. why is it not? i'm setting the header, but it's not sending. `[HeaderNames.Authorization] = $"Bearer {token}",` – DLeh May 11 '22 at 18:33
  • Your directions are backwards. Youi are doing a GET instead of a PUT. – jdweng May 12 '22 at 08:47
  • 1
    @jdweng wtf are you talking about lol. I'm doing a `[POST]` as is clearly shown in my refit api definition. – DLeh May 12 '22 at 15:20

2 Answers2

1

first try:

services.AddRefitClient<T>(new RefitSettings { AuthorizationHeaderValueGetter = () => Task.FromResult("TestToken") }).ConfigureHttpClient(ConfigureHttpClient);

second try:

services.AddRefitClient<T>().ConfigureHttpClient(ConfigureHttpClient).AddHttpMessageHandler<AuthorizationMessageHandler>();

where:

  public class AuthorizationMessageHandler : DelegatingHandler
  {
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancelToken)
    {
      HttpRequestHeaders headers = request.Headers;

      AuthenticationHeaderValue authHeader = headers.Authorization;

      if (authHeader != null)
        headers.Authorization = new AuthenticationHeaderValue(authHeader.Scheme, "TestToken");
        
      return await base.SendAsync(request, cancelToken);
    }
  }
Danut Radoaica
  • 1,860
  • 13
  • 17
  • This method is giving me the same problem. As soon as i call `base.SendAsync()`, the `request.Headers` gets cleared out. – DLeh Jun 14 '22 at 20:13
  • for the AuthorizationHeaderValueGetter scenario, try to add [Headers("Authorization: Bearer")] on AddItemToCartAsync method – Danut Radoaica Jun 14 '22 at 20:32
  • What's bonus weird is that it seems to work when i run as an API but not in a console app. I tried this method with the handler, and when i step past `base.SendAsync()`, the request object headers have the authorization removed. I wonder if there is some appsetting that is overriding them somehow that's not set in my console app – DLeh Jun 15 '22 at 12:58
0

This discussion continued on a GitHub issue for Refit. Somebody usefully pointed out that by default Authorization headers get stripped off when HttpClient follows a redirect - https://github.com/reactiveui/refit/issues/1374#issuecomment-1212776451.

I also hit the same issue described. After reading the suggestion in the GitHub issue discussion I configured my HttpClient instance that disabled auto redirects.

var httpClient = new HttpClient(new HttpClientHandler() { AllowAutoRedirect = false })

This confirmed that the server API was returning 301-Moved Permanently responses to API endpoints that looked correct from the API documentation I had been given.

Looking at how I had configured my API endpoints they were configured like /{presentationId}/cart without slash at the end.

I updated the endpoints to end with a final slash /{presentationId}/cart/ and this stopped the server redirects being returned.

Simon Needham
  • 543
  • 1
  • 9
  • 23
  • wow that is so strange... i'll have to give that a try when i get a chance. Thanks! – DLeh Feb 17 '23 at 13:38