27

I'm trying to use an HttpClient for a third-party service that requires basic HTTP authentication. I am using the AuthenticationHeaderValue. Here is what I've come up with so far:

HttpRequestMessage<RequestType> request = 
    new HttpRequestMessage<RequestType>(
        new RequestType("third-party-vendor-action"),
        MediaTypeHeaderValue.Parse("application/xml"));
request.Headers.Authorization = new AuthenticationHeaderValue(
    "Basic", Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(
        string.Format("{0}:{1}", "username", "password"))));

var task = client.PostAsync(Uri, request.Content);
ResponseType response = task.ContinueWith(
    t =>
    {
        return t.Result.Content.ReadAsAsync<ResponseType>();
    }).Unwrap().Result;

It looks like the POST action works fine, but I don't get back the data I expect. Through some trial and error, and ultimately using Fiddler to sniff the raw traffic, I discovered the authorization header isn't being sent.

I've seen this, but I think I've got the authentication scheme specified as a part of the AuthenticationHeaderValue constructor.

Is there something I've missed?

Community
  • 1
  • 1
Ross
  • 2,448
  • 1
  • 21
  • 24

5 Answers5

41

Your code looks like it should work - I remember running into a similar problem setting the Authorization headers and solved by doing a Headers.Add() instead of setting it:

request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "username", "password"))));

UPDATE: It looks like when you do a request.Content, not all headers are being reflected in the content object. You can see this by inspecting request.Headers vs request.Content.Headers. One thing you might want to try is to use SendAsync instead of PostAsync. For example:

HttpRequestMessage<RequestType> request = 
     new HttpRequestMessage<RequestType>(
         new RequestType("third-party-vendor-action"),
         MediaTypeHeaderValue.Parse("application/xml"));

request.Headers.Authorization = 
    new AuthenticationHeaderValue(
        "Basic", 
        Convert.ToBase64String(
            System.Text.ASCIIEncoding.ASCII.GetBytes(
                string.Format("{0}:{1}", "username", "password"))));

 request.Method = HttpMethod.Post;
 request.RequestUri = Uri;
 var task = client.SendAsync(request);

 ResponseType response = task.ContinueWith(
     t => 
         { return t.Result.Content.ReadAsAsync<ResponseType>(); })
         .Unwrap().Result;
Hai Nguyen
  • 4,059
  • 1
  • 20
  • 16
  • I tried that and something similar with `request.Headers.Authorization` with the same results. It's left me with a bit of a head-scratcher as I'm not entirely sure what to try next. I know the MVC4 beta and 4.5 preview are directly incompatible for some things - I wonder if that has something to do with it? – Ross Apr 17 '12 at 14:09
  • Your update seems to at least send the authorization header. I'm not yet able to retrieve data from the third party service yet (probably something else wrong in the request body I've missed), but at least the correct headers are going over the wire. It's a start. Thank you! – Ross May 17 '12 at 16:42
  • ASCII or UTF-8? See http://stackoverflow.com/questions/11743160/how-do-i-encode-and-decode-a-base64-string. – Philippe Nov 10 '14 at 20:24
21

This would also work and you wouldn't have to deal with the base64 string conversions:

var handler = new HttpClientHandler();
handler.Credentials = new System.Net.NetworkCredential("username", "password");
var client = new HttpClient(handler);
...
Nitin Agarwal
  • 3,830
  • 1
  • 21
  • 12
18

Try setting the header on the client:

DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(String.Format("{0}:{1}", userName, password))));

This works for me.

David Peden
  • 17,596
  • 6
  • 52
  • 72
  • 2
    This would probably work, but it would set the authorization headers for all requests, would it not? That's not desirable. – Ross May 17 '12 at 16:01
  • 3
    For all requests made with that instance of a client, yes. Are you using the same client to access endpoints with different authentication schemes? I.e., are you setting the BaseAddress on the client instance or not? – David Peden May 17 '12 at 16:37
8

Actually your problem is with PostAsync- you should use SendAsync. In your code - client.PostAsync(Uri, request.Content); sends only the content the request message headers are not included. The proper way is:

HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, url)
{
    Content = content
};
message.Headers.Authorization = new AuthenticationHeaderValue("Basic", credentials);
httpClient.SendAsync(message);
Indomitable
  • 808
  • 9
  • 9
  • 2
    A thousand times thank you. I've spent hours trying to attach headers to the HttpClient and using GetAsync and PostAsync etc, and in general am finding WebAPI a pain (give me ServiceStack!!) – Dale Holborow Aug 25 '16 at 13:07
8

Also, consider that Redirect-Handler will clear the Authorization header if your request gets redirected.
So if you call an HTTP endpoint and it redirected to the HTTPS one, you will lose your authorization header.

request.Headers.Authorization = null;

Framework: .NET v6.0

Søren
  • 6,517
  • 6
  • 43
  • 47