18

Currently, I am adding an authorization header to my request like this:

File: SomeFile.cs

public interface ITestApi
{
    [Get("/api/test/{id}")]
    Task<string> GetTest([Header("Authorization")] string authorization, int id);

    [Get("/api/tests/")]
    Task<string> GetTests([Header("Authorization")] string authorization);
}

File: SomeOtherFile.cs

var username = "xxx";
var password = "yyy";
var authHeader = "Basic " + Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
var baseAddress = "https://my.test.net";
ITestApi myApi = RestService.For<ITestApi>(baseAddress);

int id = 123;
var test= myApi.GetTest(authHeader, id);

var tests = myApi.GetTests(authHeader);

Is there a way to set the authHeader globally so that I don't have to pass it as a parameter to every call? (I am using Refit version 4.6.48). In other words, I'd like to be able to do the calls like this:

var test= myApi.GetTest(id);

var tests = myApi.GetTests();
Dariusz Woźniak
  • 9,640
  • 6
  • 60
  • 73
thd
  • 2,023
  • 7
  • 31
  • 45

3 Answers3

20

I found a solution:

[Headers("Authorization: Basic")]
public interface ITestApi
{
    [Get("/api/test/{id}")]
    Task<string> GetTest(int id);

    [Get("/api/tests/")]
    Task<string> GetTests();
}


var username = "xxx";
var password = "yyy";
var authHeader = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
var baseAddress = "https://my.test.net";

var refitSettings = new RefitSettings()
{
    AuthorizationHeaderValueGetter = () => Task.FromResult(authHeader)
};

ITestApi myApi = RestService.For<ITestApi>(baseAddress, refitSettings);

int id = 123;
var test= myApi.GetTest(id);

var tests = myApi.GetTests();
GaboBrandX
  • 2,380
  • 14
  • 25
thd
  • 2,023
  • 7
  • 31
  • 45
  • 2
    Great! The important part here that makes the `AuthorizationHeaderValueGetter` part work, and not get ignored, is the attribute on the interface. `[Headers("Authorization: Basic")]` that has to be there, including the `: ` part of it to Refit to invoke the `AuthorizationHeaderValueGetter`. – jonfuller Nov 19 '20 at 16:05
  • What if we want a dynamic scheme for Authorization? The "Basic" in this case. – mr5 Apr 01 '21 at 16:35
  • This is the correct answer! – Tagi Feb 28 '22 at 12:35
  • Is there a way to set the header name? – Shimmy Weitzhandler Jul 24 '23 at 16:42
8

thd's answer did not work for me because Refit is currently simply ignoring AuthorizationHeaderValueGetter and the requests do not contain the authentication header.

I could make it work by providing my HttpClient with a default authentication header:

string token = await GetTokenAsync().ConfigureAwait(false);
string endpointUrl = Environment.GetEnvironmentVariable(endpointVariable) ??
                     defaultEndpointUrl ?? DefaultEndpointUrl;

var client = new HttpClient(new HttpClientHandler())
{
    BaseAddress = new Uri(endpointUrl)
};
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

//TODO: remove as this is ignored anyway
//var refitSettings = new RefitSettings
//{
//    AuthorizationHeaderValueGetter = () => Task.FromResult($"{token}")
//};

var builder = RequestBuilder.ForType<TApiClient>();
return RestService.For(client, builder);

GetTokenAsync looks like the following:

public static async Task<string> GetAccessTokenAsync()
{
    // theoretically the token could have expired during the tests, but there is very low chance
    // so returning the same token if one was generated
    lock (SyncLock)
    {
        if (!string.IsNullOrWhiteSpace(Token))
            return Token;
    }

    var client = new HttpClient();
    var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
    {
        Address = $"{IdentityServerUrl}/connect/token",
        ClientId = "MY_CLIENT_APITEST",
        ClientSecret = IdentityServerPass,
        Scope = "api.read"

    }).ConfigureAwait(false);
    tokenResponse.HttpResponse.EnsureSuccessStatusCode();

    lock (SyncLock)
    {
        Token = tokenResponse.AccessToken;
        return Token;
    }
}
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
7

Refit has changed its approach. Now it uses the default .NET HttpHandler

var handler = new AuthHandler(credentials.Username, credentials.Password);
var githubApi = RestService.For<IGithubApi>(new HttpClient(handler)
                {
                    BaseAddress = new Uri("https://api.github.com")
                });


public class AuthHandler : HttpClientHandler
{
    private readonly string _username;
    private readonly string _password;

    public AuthHandler(string username, 
        string password)
    {
        _username = username;
        _password = password;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, 
        CancellationToken cancellationToken)
    {
        request.Headers.Add("User-Agent", "<your user name or app name>");
        request.Headers.Authorization = new AuthenticationHeaderValue("Basic",
            Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(
                $"{_username}:{_password}")));

        return await base.SendAsync(request, cancellationToken)
            .ConfigureAwait(false);
    }
}
Andrew Chaa
  • 6,120
  • 2
  • 45
  • 33
  • 2
    I cannot get this to work. Seems like refit removes authorization in header. Tested on Xamarin without DI, just direct. Any tips? – Large Jan 31 '21 at 00:01