Home > Software engineering >  NetWorkCredentials not showing up in headers
NetWorkCredentials not showing up in headers

Time:12-06

I have two projects: a Web API project and a client project.

In the client application, I configure my HttpClient like this.

services.AddHttpClient<TrackAndTraceClient>()
    .ConfigureHttpClient(httpClient =>
    {
        httpClient.BaseAddress = new Uri(settings.BaseUrl);
        httpClient.Timeout = TimeSpan.FromMinutes(5);
    })
    .ConfigurePrimaryHttpMessageHandler(serviceProvider =>
    {
        return new HttpClientHandler()
        {
            Credentials = new NetworkCredential(settings.Username, settings.Password),
        };
    });

And then in my class that calls the API:

public TrackAndTraceClient(IHttpClientFactory httpClientFactory, IOptions<TrackAndTraceSettings> settings)
{
    HttpClient = httpClientFactory.CreateClient(nameof(TrackAndTraceClient));
    Settings = settings.Value;
}

My Web API site implements basic authentication using the techniques described in this article. But my code throws an exception because no Authorization header is found.

public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {

        // ...

        if (!Request.Headers.TryGetValue("Authorization", out StringValues authHeaderValues))
            throw new Exception("Missing Authorization header");

        // ...
    }
}

I can only get this to work by adding the following code to my class that calls the API:

HttpClient.DefaultRequestHeaders.Add("ContentType", "application/json");
byte[] credentialsData = Encoding.UTF8.GetBytes($"{Settings.Username}:{Settings.Password}");
string credentials = Convert.ToBase64String(credentialsData);
HttpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {credentials}");

Can anyone tell me why this last block of code is needed? Why doesn't setting the credentials with NetworkCredential appear to do anything? And how can I change my Web API so that it works with credentials specified the original way?

Note that I'm also calling a third-party API, and the client is configured exactly the same way as in my first block of code. So I know that can be made to work.

CodePudding user response:

From our conversation in the comments section -

The implementation of your BasicAuthenticationHandler lacks adding the WWW-Authenticate HTTP header (with value Basic).

That is the header upon which the HttpClient reacts to include the Authorization HTTP header when it receives a 401 Unauthorized response.

To resolve the issue, add the line below to the BasicAuthenticationHandler.

Response.Headers.Add("WWW-Authenticate", "Basic");

Now the NetworkCredentials will go into the Authorization HTTP header.


In short without being complete about how this works;
when a HttpClient makes a request (without Authorization header), and receives a 401 Unauthorized response in combination with a WWW-Authenticate HTTP header, it will make a 2nd attempt with the configured credentials - if any - in the Authorization HTTP header.


For simplicity, you might want to include the Authorization at once, without relying on NetworkCredentials and WWW-Authenticate HTTP headers.

services.AddHttpClient<TrackAndTraceClient>()
    .ConfigureHttpClient(httpClient =>
    {
        httpClient.BaseAddress = new Uri(settings.BaseUrl);
        httpClient.Timeout = TimeSpan.FromMinutes(5);

        // Add below to your existing code.
        var digest = Convert.ToBase64String(
            Encoding.UTF8.GetBytes($"{settings.Username}:{settings.Password}")
            );  
        httpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {digest}");
        HttpClient.DefaultRequestHeaders.Add("ContentType", "application/json");        
    });
  • Related