How do you use Basic Authentication with System.Net.Http.HttpClient?

It looks like you can't use PostAsync and have access to mess with the Headers for authentication. I had to use an HttpRequestMessage and SendAsync.

//setup reusable http client
HttpClient client = new HttpClient();
Uri baseUri = new Uri(url);
client.BaseAddress = baseUri;
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.ConnectionClose = true;

//Post body content
var values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
var content = new FormUrlEncodedContent(values);

var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(authenticationString));

var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/oauth2/token");
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", base64EncodedAuthenticationString);
requestMessage.Content = content;

//make the request
var task = client.SendAsync(requestMessage);
var response = task.Result;
response.EnsureSuccessStatusCode();
string responseBody = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseBody);

The specific problem is this line (below)

content.Headers.Add("Authorization", $"Basic {base64EncodedAuthenticationString}");

This fails because HttpContent.Headers (System.Net.Http.Headers.HttpContentHeaders) is only for headers that are content-specific, such as Content-Type, Content-Length, and so on.

You've stated that you can't use DefaultRequestHeaders because you only need it for a single request - but you also can't use it with PostAsync - only SendAsync provided you construct the HttpRequestMessage yourself, as per your own answer and @NeilMoss' answer - but you could use an extension-method in future.

But for the benefit of other readers, another alternative is to add a new extension method based on the existing PostAsync, which is actually really simple (only 3 lines!):

public Task<HttpResponseMessage> PostAsync( this HttpClient httpClient, Uri requestUri, HttpContent content, String basicUserName, String basicPassword, String? challengeCharSet = null, CancellationToken cancellationToken = default )
{
    if( basicUserName.IndexOf(':') > -1 ) throw new ArgumentException( message: "RFC 7617 states that usernames cannot contain colons.", paramName: nameof(basicUserName) );

    HttpRequestMessage httpRequestMessage = new HttpRequestMessage( HttpMethod.Post, requestUri );
    httpRequestMessage.Content = content;

    //

    Encoding encoding = Encoding.ASCII;
    if( challengeCharSet != null )
    {
        try
        {
            encoding = Encoding.GetEncoding( challengeCharSet );
        }
        catch
        {
            encoding = Encoding.ASCII;
        }
    }

    httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue(
        scheme   : "Basic",
        parameter: Convert.ToBase64String( encoding.GetBytes( userName + ":" + password ) )
    );

    return SendAsync( httpRequestMessage, cancellationToken );
}

Usage:

HttpClient httpClient = ...

using( HttpResponseMessage response = await httpClient.PostAsync( uri, content, basicUserName: "AzureDiamond", basicPassword: "hunter2" ).ConfigureAwait(false) )
{
    // ...
}

Don't encode the whole authentication string - encode the "Username:Password" expression and append the result to the "Basic " prefix.

var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.UTF8.GetBytes(authenticationString));
content.Headers.Add("Authorization", "Basic " + base64EncodedAuthenticationString);

Also, consider using just ASCII encoding - the UTF8 may not be understood by the server unless you add a charset declaration to the header.

Wikipedia seems to cover this quite well.


It's not a good practice to create HttpClients explicitly from your calling code. Please use HttpClientFactory that simplifies a lot of things.

However, if you want to use basic authentication, just create an HttpRequestMessage and add the following header:

var request = new HttpRequestMessage(HttpMethod.Post, getPath)
{
    Content = new FormUrlEncodedContent(values)
};
request.Headers.Authorization = new BasicAuthenticationHeaderValue("username", "password");
// other settings

If you decide to use a recommended IHttpClientFactory it's even simpler:

serviceCollection.AddHttpClient(c =>
{
   c.BaseAddress = new Uri("your base url");
   c.SetBasicAuthentication("username", "password");
})