813

I have an HttpClient that I am using for a REST API. However I am having trouble setting up the Authorization header. I need to set the header to the token I received from doing my OAuth request. I saw some code for .NET that suggests the following,

httpClient.DefaultRequestHeaders.Authorization = new Credential(OAuth.token);

However the Credential class does that not exist in WinRT. Anyone have any ideas how to set the Authorization header?

John
  • 2,395
  • 15
  • 21
Stephen Hynes
  • 22,071
  • 6
  • 18
  • 20

26 Answers26

1312

So the way to do it is the following,

httpClient.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "Your Oauth token");
Ryan Lundy
  • 204,559
  • 37
  • 180
  • 211
Stephen Hynes
  • 22,071
  • 6
  • 18
  • 20
  • 34
    how do you get "Your Oauth token"? – Squirrel5853 Jul 14 '17 at 10:56
  • 13
    What I've used is: `client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "encrypted user/pwd");` Taking encrypted user/pwd from Advanced Rest Client chrome extension. – Red Aug 01 '17 at 01:17
  • 12
    @Red fyi, the second parameter is the base64 encoded user:password (its not encrypted). – n00b Aug 01 '17 at 19:11
  • Beware: this might result in ArgumentNullException, see https://stackoverflow.com/a/47975423/14131 – Pavel Chuchuva Feb 21 '18 at 23:57
  • 6
    My application was happily using this for ages, then out of the blue I started getting a RuntimeBinderException. I had to switch to _httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer", "Your Oauth token");_ to get it going again. – Craig May 25 '18 at 03:39
  • 1
    Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The best overloaded method match for 'System.Net.Http.Headers.AuthenticationHeaderValue.AuthenticationHeaderValue(string, string)' has some invalid arguments – Craig May 25 '18 at 03:40
  • @kraeg same thing happened to me. Switching worked, but I found out that my authtoken was a jobject and the fix you mentioned must have cast it correctly. When I properly cast the jobject to a string the original method worked again. – RyanOC Dec 21 '18 at 14:20
  • 16
    @kraeg, the code you listed doesn't compile, did you mean to concatenate the last 2 strings like so: client.DefaultRequestHeaders.Add("Authorization", "Bearer " + "Your Oauth token"); – TroySteven Mar 18 '19 at 21:50
  • 1
    @matwonk I reckon you must be right. I can't find that piece of code anywhere, but I do find plenty of the concatenated version. Sorry about that. – Craig Mar 20 '19 at 21:55
  • What if there are two tokens that I need to pass? – Najeeb Jan 14 '20 at 05:09
  • @SecretSquirrel var token = await HttpContext.GetTokenAsync("access_token"); – Sumesh Es Mar 23 '22 at 18:02
461
request.DefaultRequestHeaders.Authorization = 
    new AuthenticationHeaderValue(
        "Basic", Convert.ToBase64String(
            System.Text.ASCIIEncoding.ASCII.GetBytes(
               $"{yourusername}:{yourpwd}")));
Otabek Kholikov
  • 1,188
  • 11
  • 19
TheWhiteRabbit
  • 15,480
  • 4
  • 33
  • 57
  • 3
    This is not working, if you inspect the Auhtorization header is does not contains anything more than a string Basic. – Raffaeu Oct 31 '14 at 11:59
  • 2
    Can anyone explain why it's important to convert the username and password to a base64 string? It offers no real encryption, so why does that matter? – Jonathan Wood Jan 20 '17 at 03:10
  • 4
    @JonathanWood Because that;s how it is defined to be used. Basic offers no encryption, just enough encoding to avoid issues with choice of password characters in a header. – Richard Jul 20 '17 at 13:22
  • 9
    Is there any particular reason you used ASCII encoding here? I assume there is no issue with using UTF8 encoding since we are Base64 encoding it anyways. I guess I'm wondering if the Basic authentication specification says that the username:password combo should be in ASCII only? – crush Feb 20 '18 at 20:44
  • @crush See https://stackoverflow.com/a/7243567/1399272. It seems like UTF-8 is a valid option going forward (i.e. modern browsers [i.e. not IE]) – Bondolin May 20 '19 at 21:09
  • 4
    System.Text.ASCIIEncoding.ASCII is actually in the parent class Encoding. So you can use System.Text.Encoding.ASCII instead. – David Klempfner Sep 10 '19 at 00:16
  • This does not answer the question. The user is asking about using a Barer token to authenticate, not Basic Auth – Theo Aug 10 '22 at 17:07
119

I look for a good way to deal with this issue and I am looking at the same question. Hopefully, this answer will be helping everyone who has the same problem likes me.

using (var client = new HttpClient())
{
    var url = "https://www.theidentityhub.com/{tenant}/api/identity/v1";
    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
    var response = await client.GetStringAsync(url);
    // Parse JSON response.
    ....
}

reference from https://www.theidentityhub.com/hub/Documentation/CallTheIdentityHubApi

Willie Cheng
  • 7,679
  • 13
  • 55
  • 68
  • 3
    I'm doing the exact same thing @willie and I'm still getting a 401 from my API – SomethingOn Oct 22 '15 at 22:07
  • 2
    Hi @SomethingOn I think you didn't get a correct token key ,so that you got 401 , I will share my way on my personal "Ask Question" , hopefully it can help you to deal with your problem.PS waiting for a moment – Willie Cheng Oct 28 '15 at 10:56
  • 34
    You shouldn't put an HttpClient in a `using` block. (Yes, I know it sounds backwards, but you'll leak connections if you use `using` instead of just recycling the HttpClient.) – Jonathan Allen Oct 29 '18 at 22:43
  • @JonathanAllen if you're referring to connection leak described [here](https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/), it may be worth noting that few readers have mentioned in article comments that the leak might be related to how responses are handled, since many developers forget to dispose the `HttpResponse` itself and only dispose of the `HttpClient`. – masiton Dec 04 '20 at 06:46
  • Also note that `client.DefaultRequestHeaders.Add` will throw an exception if you end up calling it multiple times with the same header name. (Trying to add same header multiple times.) – Joshua Enfield May 26 '22 at 15:55
  • You should not put `HttpClient` into a `using` statement because it will eventually cause socket exhaustion problems. Here's why: https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong – Tony Oct 17 '22 at 18:33
87

As it is a good practice to reuse the HttpClient instance, for performance and port exhaustion problems, and because none of the answers give this solution (and even leading you toward bad practices :( ), I put here a link towards the answer I made on a similar question :

https://stackoverflow.com/a/40707446/717372

Some sources on how to use HttpClient the right way:

Philippe
  • 28,207
  • 6
  • 54
  • 78
  • 17
    The port exhaustion problem is no joke. It almost never happens in QA, but will hit any heavily used project in production. – Jonathan Allen Oct 29 '18 at 22:45
  • See my post for a concrete example https://stackoverflow.com/a/59052193/790635 – emp Feb 10 '20 at 15:12
  • 3
    It's almost laughable that this criticism of setting the default auth in the client is so far down the thread. I was pretty much scratching my head as to why I need to set an authorization on the client itself. – zireael9797 Aug 08 '22 at 10:10
54

I suggest to you:

HttpClient.DefaultRequestHeaders.Add("Authorization", "Bearer <token>");

And then you can use it like that:

var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
    responseMessage = await response.Content.ReadAsAsync<ResponseMessage>();
}
  • 3
    If your token times out every 1h for example then you have to update the HttpClient with this solution. I would suggest checking that your token is still valid and otherwise refreshing it and adding it to the HttpRequestMessage – Johan Franzén May 20 '20 at 09:29
50

In the case you want to send HttpClient request with Bearer Token, this code can be a good solution:

var requestMessage = new HttpRequestMessage
{
    Method = HttpMethod.Post,
    Content = new StringContent(".....", Encoding.UTF8, "application/json"),
    RequestUri = new Uri(".....")
};

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

var response = await _httpClient.SendAsync(requestMessage);
Jourmand
  • 888
  • 10
  • 27
27

I was setting the bearer token

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

It was working in one endpoint, but not another. The issue was that I had lower case b on "bearer". After change now it works for both api's I'm hitting. Such an easy thing to miss if you aren't even considering it as one of the haystacks to look in for the needle.

Make sure to have "Bearer" - with capital.

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
Alan Ball
  • 593
  • 8
  • 17
24

Use Basic Authorization And Json Parameters.

using (HttpClient client = new HttpClient())
{
    var request_json = "your json string";

    var content = new StringContent(request_json, Encoding.UTF8, "application/json");

    var authenticationBytes = Encoding.ASCII.GetBytes("YourUsername:YourPassword");

    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
            Convert.ToBase64String(authenticationBytes));
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    var result = await client.PostAsync("YourURL", content);

    var result_string = await result.Content.ReadAsStringAsync();
}
James Skemp
  • 8,018
  • 9
  • 64
  • 107
MohammadSoori
  • 2,120
  • 1
  • 15
  • 17
  • 8
    You shouldn't include code to disable checking of SSL certificates in an example like this. People may blindly copy your code not realising what it does. I've removed those lines for you. – ProgrammingLlama Apr 13 '18 at 07:01
  • 1
    You should not put `HttpClient` into a `using` statement because it will eventually cause socket exhaustion problems. Here's why: https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong – Tony Oct 17 '22 at 18:32
20

For anyone finding this old thread now (2021), please look at this documentation about HttpClientFactory which is injectable and will also re-run on each request avoiding expired tokens which will make it useful for bearer tokens, generated clients, pooling etc.

TL;DR: Use HttpClientFactory and a DelegatingHandler which will act as middleware on all outgoing requests with your configured client.

This is how I add my bearer for Azure Identity (managed by Azure) but you can get the token however you want of course;

using Microsoft.Azure.Services.AppAuthentication;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public class BearerTokenHandler : DelegatingHandler
    {
        public BearerTokenHandler(AzureServiceTokenProvider tokenProvider, string resource)
        {
            TokenProvider = tokenProvider;
            Resource = resource;
        }

        public AzureServiceTokenProvider TokenProvider { get; }
        public string Resource { get; }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (!request.Headers.Contains("Authorization"))
            {
                // Fetch your token here
                string token = await TokenProvider.GetAccessTokenAsync(Resource);
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
            }

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

I configure my typed clients (generated with NSwag) like this in Startup;

   var accessTokenProvider = new AzureServiceTokenProvider("<your-connection-string-for-access-token-provider>");

  builder.Services.AddHttpClient<IOrdersClient, OrdersClient>().ConfigureHttpClient(async conf =>
            {
                conf.BaseAddress = new Uri("<your-api-base-url>");
            }).AddHttpMessageHandler(() => new BearerTokenHandler(accessTokenProvider, "https://your-azure-tenant.onmicrosoft.com/api"));

Then you can inject your IOrdersClient wherever you like and all requests will have the bearer.

Jonas Stensved
  • 14,378
  • 5
  • 51
  • 80
16

If you want to reuse the HttpClient, it is advised to not use the DefaultRequestHeaders as they are used to send with each request.

You could try this:

var requestMessage = new HttpRequestMessage
    {
        Method = HttpMethod.Post,
        Content = new StringContent("...", Encoding.UTF8, "application/json"),
        RequestUri = new Uri("...")
    };

requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", 
    Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes($"{user}:{password}")));

var response = await _httpClient.SendAsync(requestMessage);
emp
  • 4,926
  • 2
  • 38
  • 50
  • 3
    Yeah, surprisingly hard to find this answer. I guess many don't read the docs much because best practice is to have HttpClient be a static member variable to avoid port exhaustion issues. And then it makes little sense to use DefaultRequestHeaders either, _especially_ if we're talking token/bearer authentication like many do here because these tokens will inevitably expire! So basing a default on that is just backwards. – Jonas Sep 30 '21 at 10:19
14

To set basic authentication with C# HttpClient. The following code is working for me.

   using (var client = new HttpClient())
        {
            var webUrl ="http://localhost/saleapi/api/";
            var uri = "api/sales";
            client.BaseAddress = new Uri(webUrl);
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            client.DefaultRequestHeaders.ConnectionClose = true;

            //Set Basic Auth
            var user = "username";
            var password = "password";
            var base64String =Convert.ToBase64String( Encoding.ASCII.GetBytes($"{user}:{password}"));
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",base64String);

            var result = await client.PostAsJsonAsync(uri, model);
            return result;
        }
LENG UNG
  • 473
  • 6
  • 5
  • What is "model" in your code? – motevalizadeh Dec 26 '21 at 00:39
  • You should not put `HttpClient` into a `using` statement because it will eventually cause socket exhaustion problems. Here's why: https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong – Tony Oct 17 '22 at 18:33
10

This is how i have done it:

using (HttpClient httpClient = new HttpClient())
{
   Dictionary<string, string> tokenDetails = null;
   var messageDetails = new Message { Id = 4, Message1 = des };
   HttpClient client = new HttpClient();
   client.BaseAddress = new Uri("http://localhost:3774/");
   var login = new Dictionary<string, string>
       {
           {"grant_type", "password"},
           {"username", "sa@role.com"},
           {"password", "lopzwsx@23"},
       };
   var response = client.PostAsync("Token", new FormUrlEncodedContent(login)).Result;
   if (response.IsSuccessStatusCode)
   {
      tokenDetails = JsonConvert.DeserializeObject<Dictionary<string, string>>(response.Content.ReadAsStringAsync().Result);
      if (tokenDetails != null && tokenDetails.Any())
      {
         var tokenNo = tokenDetails.FirstOrDefault().Value;
         client.DefaultRequestHeaders.Add("Authorization", "Bearer " + tokenNo);
         client.PostAsJsonAsync("api/menu", messageDetails)
             .ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode());
      }
   }
}

This you-tube video help me out a lot. Please check it out. https://www.youtube.com/watch?v=qCwnU06NV5Q

Dayan
  • 369
  • 1
  • 5
  • 12
8

6 Years later but adding this in case it helps someone.

https://www.codeproject.com/Tips/996401/Authenticate-WebAPIs-with-Basic-and-Windows-Authen

var authenticationBytes = Encoding.ASCII.GetBytes("<username>:<password>");
using (HttpClient confClient = new HttpClient())
{
  confClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", 
         Convert.ToBase64String(authenticationBytes));
  confClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Constants.MediaType));  
  HttpResponseMessage message = confClient.GetAsync("<service URI>").Result;
  if (message.IsSuccessStatusCode)
  {
    var inter = message.Content.ReadAsStringAsync();
    List<string> result = JsonConvert.DeserializeObject<List<string>>(inter.Result);
  }
}
MPJ567
  • 521
  • 5
  • 16
5

UTF8 Option

request.DefaultRequestHeaders.Authorization = 
new AuthenticationHeaderValue(
    "Basic", Convert.ToBase64String(
        System.Text.Encoding.UTF8.GetBytes(
           $"{yourusername}:{yourpwd}")));
romelmederos
  • 198
  • 3
  • 5
4

In net .core you can use with Identity Server 4

var client = new HttpClient();
client.SetBasicAuthentication(userName, password);

or

var client = new HttpClient();
client.SetBearerToken(token);

see https://github.com/IdentityModel/IdentityModel/blob/main/src/Client/Extensions/AuthorizationHeaderExtensions.cs

Thom Kiesewetter
  • 6,703
  • 3
  • 28
  • 41
  • 4
    First example does not work as `SetBasicAuthentication()` is not available by default so it has to be a extension method. Where is it defined? – ViRuSTriNiTy Nov 28 '19 at 14:09
  • 1
    @ViRuSTriNiTy Install the `IdentityModel` package first, then `using IdentityModel.Client;`. – Will Huang Apr 10 '22 at 08:02
3

Using AuthenticationHeaderValue class of System.Net.Http assembly

public AuthenticationHeaderValue(
    string scheme,
    string parameter
)

we can set or update existing Authorization header for our httpclient like so:

httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", TokenResponse.AccessToken);
iiminov
  • 969
  • 1
  • 15
  • 34
Fusion
  • 165
  • 2
  • 8
3

BaseWebApi.cs

public abstract class BaseWebApi
{
    //Inject HttpClient from Ninject
    private readonly HttpClient _httpClient;
    public BaseWebApi(HttpClient httpclient)
    {
        _httpClient = httpClient;
    }

    public async Task<TOut> PostAsync<TOut>(string method, object param, Dictionary<string, string> headers, HttpMethod httpMethod)
    {
        //Set url

        HttpResponseMessage response;
        using (var request = new HttpRequestMessage(httpMethod, url))
        {
            AddBody(param, request);
            AddHeaders(request, headers);
            response = await _httpClient.SendAsync(request, cancellationToken);
        }

        if(response.IsSuccessStatusCode)
        {
             return await response.Content.ReadAsAsync<TOut>();
        }
        //Exception handling
    }

    private void AddHeaders(HttpRequestMessage request, Dictionary<string, string> headers)
    {
        request.Headers.Accept.Clear();
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        if (headers == null) return;

        foreach (var header in headers)
        {
            request.Headers.Add(header.Key, header.Value);
        }
    }

    private static void AddBody(object param, HttpRequestMessage request)
    {
        if (param != null)
        {
            var content = JsonConvert.SerializeObject(param);
            request.Content = new StringContent(content);
            request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
        }
    }

SubWebApi.cs

public sealed class SubWebApi : BaseWebApi
{
    public SubWebApi(HttpClient httpClient) : base(httpClient) {}

    public async Task<StuffResponse> GetStuffAsync(int cvr)
    {
        var method = "get/stuff";
        var request = new StuffRequest 
        {
            query = "GiveMeStuff"
        }
        return await PostAsync<StuffResponse>(method, request, GetHeaders(), HttpMethod.Post);
    }
    private Dictionary<string, string> GetHeaders()
    {
        var headers = new Dictionary<string, string>();
        var basicAuth = GetBasicAuth();
        headers.Add("Authorization", basicAuth);
        return headers;
    }

    private string GetBasicAuth()
    {
        var byteArray = Encoding.ASCII.GetBytes($"{SystemSettings.Username}:{SystemSettings.Password}");
        var authString = Convert.ToBase64String(byteArray);
        return $"Basic {authString}";
    }
}
Joel Wiklund
  • 1,697
  • 2
  • 18
  • 24
2

You can too to use the follow exemple, that it use IHttpClientFactory:

    readonly IHttpClientFactory _httpClientFactory;
    
    public HTTPClientHelper(IHttpClientFactory httpClientFactory, string clientName = null)
    {
        this._httpClientFactory = httpClientFactory;
    }

    public Task<T> GetAsync(string url, string token) {

        var client = _httpClientFactory.CreateClient(_clientName);

        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(JwtBearerDefaults.AuthenticationScheme, token);

        using (HttpResponseMessage response = await _client.GetAsync(url)){
          ......
        }
     }
1

this could works, if you are receiving a json or an xml from the service and i think this can give you an idea about how the headers and the T type works too, if you use the function MakeXmlRequest(put results in xmldocumnet) and MakeJsonRequest(put the json in the class you wish that have the same structure that the json response) in the next way

/*-------------------------example of use-------------*/
MakeXmlRequest<XmlDocument>("your_uri",result=>your_xmlDocument_variable =     result,error=>your_exception_Var = error);

MakeJsonRequest<classwhateveryouwant>("your_uri",result=>your_classwhateveryouwant_variable=result,error=>your_exception_Var=error)
/*-------------------------------------------------------------------------------*/


public class RestService
{
    public void MakeXmlRequest<T>(string uri, Action<XmlDocument> successAction, Action<Exception> errorAction)
    {
        XmlDocument XMLResponse = new XmlDocument();
        string wufooAPIKey = ""; /*or username as well*/
        string password = "";
        StringBuilder url = new StringBuilder();
        url.Append(uri);
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url.ToString());
        string authInfo = wufooAPIKey + ":" + password;
        authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
        request.Timeout = 30000;
        request.KeepAlive = false;
        request.Headers["Authorization"] = "Basic " + authInfo;
        string documento = "";
        MakeRequest(request,response=> documento = response,
                            (error) =>
                            {
                             if (errorAction != null)
                             {
                                errorAction(error);
                             }
                            }
                   );
        XMLResponse.LoadXml(documento);
        successAction(XMLResponse);
    }



    public void MakeJsonRequest<T>(string uri, Action<T> successAction, Action<Exception> errorAction)
    {
        string wufooAPIKey = "";
        string password = "";
        StringBuilder url = new StringBuilder();
        url.Append(uri);
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url.ToString());
        string authInfo = wufooAPIKey + ":" + password;
        authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
        request.Timeout = 30000;
        request.KeepAlive = false;
        request.Headers["Authorization"] = "Basic " + authInfo;
       // request.Accept = "application/json";
      //  request.Method = "GET";
        MakeRequest(
           request,
           (response) =>
           {
               if (successAction != null)
               {
                   T toReturn;
                   try
                   {
                       toReturn = Deserialize<T>(response);
                   }
                   catch (Exception ex)
                   {
                       errorAction(ex);
                       return;
                   }
                   successAction(toReturn);
               }
           },
           (error) =>
           {
               if (errorAction != null)
               {
                   errorAction(error);
               }
           }
        );
    }
    private void MakeRequest(HttpWebRequest request, Action<string> successAction, Action<Exception> errorAction)
    {
        try{
            using (var webResponse = (HttpWebResponse)request.GetResponse())
            {
                using (var reader = new StreamReader(webResponse.GetResponseStream()))
                {
                    var objText = reader.ReadToEnd();
                    successAction(objText);
                }
            }
        }catch(HttpException ex){
            errorAction(ex);
        }
    }
    private T Deserialize<T>(string responseBody)
    {
        try
        {
            var toReturns = JsonConvert.DeserializeObject<T>(responseBody);
             return toReturns;
        }
        catch (Exception ex)
        {
            string errores;
            errores = ex.Message;
        }
        var toReturn = JsonConvert.DeserializeObject<T>(responseBody);
        return toReturn;
    }
}
}
1

It may be easier to use an existing library.

For example, the extension methods below are added with Identity Server 4 https://www.nuget.org/packages/IdentityModel/

 public static void SetBasicAuthentication(this HttpClient client, string userName, string password);
    //
    // Summary:
    //     Sets a basic authentication header.
    //
    // Parameters:
    //   request:
    //     The HTTP request message.
    //
    //   userName:
    //     Name of the user.
    //
    //   password:
    //     The password.
    public static void SetBasicAuthentication(this HttpRequestMessage request, string userName, string password);
    //
    // Summary:
    //     Sets a basic authentication header for RFC6749 client authentication.
    //
    // Parameters:
    //   client:
    //     The client.
    //
    //   userName:
    //     Name of the user.
    //
    //   password:
    //     The password.
    public static void SetBasicAuthenticationOAuth(this HttpClient client, string userName, string password);
    //
    // Summary:
    //     Sets a basic authentication header for RFC6749 client authentication.
    //
    // Parameters:
    //   request:
    //     The HTTP request message.
    //
    //   userName:
    //     Name of the user.
    //
    //   password:
    //     The password.
    public static void SetBasicAuthenticationOAuth(this HttpRequestMessage request, string userName, string password);
    //
    // Summary:
    //     Sets an authorization header with a bearer token.
    //
    // Parameters:
    //   client:
    //     The client.
    //
    //   token:
    //     The token.
    public static void SetBearerToken(this HttpClient client, string token);
    //
    // Summary:
    //     Sets an authorization header with a bearer token.
    //
    // Parameters:
    //   request:
    //     The HTTP request message.
    //
    //   token:
    //     The token.
    public static void SetBearerToken(this HttpRequestMessage request, string token);
    //
    // Summary:
    //     Sets an authorization header with a given scheme and value.
    //
    // Parameters:
    //   client:
    //     The client.
    //
    //   scheme:
    //     The scheme.
    //
    //   token:
    //     The token.
    public static void SetToken(this HttpClient client, string scheme, string token);
    //
    // Summary:
    //     Sets an authorization header with a given scheme and value.
    //
    // Parameters:
    //   request:
    //     The HTTP request message.
    //
    //   scheme:
    //     The scheme.
    //
    //   token:
    //     The token.
    public static void SetToken(this HttpRequestMessage request, string scheme, string token);
Lee Smith
  • 6,339
  • 6
  • 27
  • 34
1

Firstly, I wouldn't use HttpClient directly. It's too easy to make mistakes - particularly in the area of headers. The DefaultHeadersCollection is not immutable and not thread-safe because other parts of the app can change the headers on you. It's best to set the headers when you make the call. If you are working with an abstraction, and that is recommended because the classes in this area are a bit of a mess, you would want to have a headers collection and put those on your HttpRequestMessage before you send it. You need to make sure you put the content headers on the content, and not the message.

Code Reference

foreach (var headerName in request.Headers.Names)
{
    //"Content-Type"
    if (string.Compare(headerName, HeadersExtensions.ContentTypeHeaderName, StringComparison.OrdinalIgnoreCase) == 0)
    {
        //Note: not sure why this is necessary...
        //The HttpClient class seems to differentiate between content headers and request message headers, but this distinction doesn't exist in the real world...
        //TODO: Other Content headers
        httpContent?.Headers.Add(HeadersExtensions.ContentTypeHeaderName, request.Headers[headerName]);
    }
    else
    {
        httpRequestMessage.Headers.Add(headerName, request.Headers[headerName]);
    }
}

Here is a data structure that you could use to send the request which includes the headers.

Code Reference

public interface IRequest
{
    CancellationToken CancellationToken { get; }
    string? CustomHttpRequestMethod { get; }
    IHeadersCollection Headers { get; }
    HttpRequestMethod HttpRequestMethod { get; }
    AbsoluteUrl Uri { get; }
}

public interface IRequest<TBody> : IRequest
{
    TBody? BodyData { get; }
}

And, a headers collection:

Code Reference

public sealed class HeadersCollection : IHeadersCollection
{
    #region Fields
    private readonly IDictionary<string, IEnumerable<string>> dictionary;
    #endregion

    #region Public Constructors

    public HeadersCollection(IDictionary<string, IEnumerable<string>> dictionary) => this.dictionary = dictionary;

    public HeadersCollection(string key, string value) : this(ImmutableDictionary.CreateRange(
                new List<KeyValuePair<string, IEnumerable<string>>>
                {
                    new(key, ImmutableList.Create(value))
                }
                ))
    {
    }

    #endregion Public Constructors

    #region Public Properties
    public static HeadersCollection Empty { get; } = new HeadersCollection(ImmutableDictionary.Create<string, IEnumerable<string>>());
    public IEnumerable<string> Names => dictionary.Keys;
    IEnumerable<string> IHeadersCollection.this[string name] => dictionary[name];
    #endregion Public Properties

    #region Public Methods
    public bool Contains(string name) => dictionary.ContainsKey(name);

    public IEnumerator<KeyValuePair<string, IEnumerable<string>>> GetEnumerator() => dictionary.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => dictionary.GetEnumerator();
    public override string ToString() => string.Join("\r\n", dictionary.Select(kvp => $"{kvp.Key}: {string.Join(", ", kvp.Value)}\r\n"));
    #endregion
}

See all the working code and examples here.

Christian Findlay
  • 6,770
  • 5
  • 51
  • 103
1

I came across this old thread. The problem I had was that I know to use a static HttpClient, but my token needs refreshing every 59 minutes.

So I could have used HttpClientFactory, but because one of my projects was still in .NET 4.8, I created a class that inherited from HttpClient so I have similar code in all projects. A secret is needed to be able to get the token (I'm using identityserver4).

I then set that as a singleton in DI (I'm using Ninject here):

Bind<MyHttpClient>().ToMethod(c =>
{
    var accessKey = ConfigurationManager.AppSettings["AccessKey"];

    var client = new MyHttpClient(accessKey)
    {
        BaseAddress = new Uri(MyUrls.MyApiBaseUrl)
    };

    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

    return client;
}).InSingletonScope();

Then the class itself - named after the API it is used to access:

public class MyHttpClient : BaseHttpClient
{
     private static readonly HttpClient _authHttpClient = new HttpClient();
     private string _secret;

     public MyHttpClient(string secret)
     {
         _secret = secret;
     }

    /// <summary>
    /// Add the token to each and every request, cached for 1 minute less than the token's lifetime
    /// </summary>
    /// <param name="request"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();

        var cacheSeconds = 3600 - 60; // Default of 59 minutes

        var token = CacheHelper<string>.Get("MyToken", cacheSeconds * 60, () =>
        {
            var authorityUrl = MyUrls.AuthServerUrl;

            // discover endpoints from metadata
            DiscoveryDocumentResponse disco;
            disco = _authHttpClient.GetDiscoveryDocumentAsync(authorityUrl).Result;
            if (disco.IsError)
            {
                throw new Exception("Error getting discovery document: " + disco.Error);
            }

            // request token
            var tokenResponse = _authHttpClient.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
            {
                Address = disco.TokenEndpoint,

                ClientId = "myapp",
                ClientSecret = _secret,
                Scope = "myapi"
            }).Result;

            if (tokenResponse.IsError)
            {
                throw new Exception("Error getting token: " + tokenResponse.Error);
            }

            if (tokenResponse.ExpiresIn < cacheSeconds + 60)
            {
                throw new Exception($"Token expires in {tokenResponse.ExpiresIn}s, which is less than {cacheSeconds + 60}");
            }

            if (tokenResponse.ExpiresIn > cacheSeconds + 60)
            {
                Log.Warn().Message($"Token expiry in {tokenResponse.ExpiresIn}s, which is greater than {cacheSeconds}").Write();
            }

            return tokenResponse.AccessToken;
        });

        // THIS IS THE BIT - Assign this inside a SendAsync override and you are done!
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        return base.SendAsync(request, cancellationToken);
    }

}

Finally just for completeness, my CacheHelper class looks like this:

public static class CacheHelper<T>
{
    private static readonly object _locker = new object();

    public static T Get(string cacheName, int cacheTimeoutSeconds, Func<T> func)
    {
        var obj = MemoryCache.Default.Get(cacheName, null);
        if (obj != null) return (T)obj;

        lock (_locker)
        {
            obj = MemoryCache.Default.Get(cacheName, null);
            if (obj == null)
            {
                obj = func();
                var cip = new CacheItemPolicy
                {
                    AbsoluteExpiration = new DateTimeOffset(DateTime.UtcNow.AddSeconds(cacheTimeoutSeconds))
                };
                MemoryCache.Default.Set(cacheName, obj, cip);
            }
        }

        return (T)obj;
    }
}
Philip Johnson
  • 1,091
  • 10
  • 24
0

Oauth Process flow is complex and there is always a room for one error or another. My suggestion will be to always use the boilerplate code and a set of libraries for OAuth authentication flow.It will make your life easier.

Here is the link for the set of libraries.OAuth Libraries for .Net

0

If you are using Visual Studio IISExpress debug mode and connecting to the HTTP port rather than the HTTPS port you may find that the auth headers are being dropped.

Switch to the SLL connection and they will appear again.

unsure why, possibly the setup redirects the http traffic and that causes the auth to be removed.

Ewan
  • 1,261
  • 1
  • 14
  • 25
-1

This may help Setting the header:

WebClient client = new WebClient();

string authInfo = this.credentials.UserName + ":" + this.credentials.Password;
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
client.Headers["Authorization"] = "Basic " + authInfo;
Codehelp
  • 4,157
  • 9
  • 59
  • 96
  • 11
    He is using [`HttpClient`](http://msdn.microsoft.com/en-us/library/system.net.http.httpclient.aspx), not [`WebClient`](http://msdn.microsoft.com/en-us/library/system.net.webclient.aspx). – Jean Hominal Jun 27 '14 at 09:05
-1
static async Task<AccessToken> GetToken()
{
        string clientId = "XXX";
        string clientSecret = "YYY";
        string credentials = String.Format("{0}:{1}", clientId, clientSecret);

        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials)));
            List<KeyValuePair<string, string>> requestData = new List<KeyValuePair<string, string>>();
            requestData.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
            FormUrlEncodedContent requestBody = new FormUrlEncodedContent(requestData);
            var request = await client.PostAsync("https://accounts.spotify.com/api/token", requestBody);
            var response = await request.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<AccessToken>(response);
        }
    }
TResponse
  • 3,940
  • 7
  • 43
  • 63
  • 1
    Welcome to stackoverflow. In addition to the answer you've provided, please consider providing a brief explanation of why and how this fixes the issue. – jtate Nov 19 '19 at 22:15