Search code examples
c#asp.net-web-apiimpersonationwindows-security

How to get HttpClient to pass credentials along with the request?


I have a web application (hosted in IIS) that talks to a Windows service. The Windows service is using the ASP.Net MVC Web API (self-hosted), and so can be communicated with over http using JSON. The web application is configured to do impersonation, the idea being that the user who makes the request to the web application should be the user that the web application uses to make the request to the service. The structure looks like this:

(The user highlighted in red is the user being referred to in the examples below.)


The web application makes requests to the Windows service using an HttpClient:

var httpClient = new HttpClient(new HttpClientHandler() 
                      {
                          UseDefaultCredentials = true
                      });
httpClient.GetStringAsync("http://localhost/some/endpoint/");

This makes the request to the Windows service, but does not pass the credentials over correctly (the service reports the user as IIS APPPOOL\ASP.NET 4.0). This is not what I want to happen.

If I change the above code to use a WebClient instead, the credentials of the user are passed correctly:

WebClient c = new WebClient
                   {
                       UseDefaultCredentials = true
                   };
c.DownloadStringAsync(new Uri("http://localhost/some/endpoint/"));

With the above code, the service reports the user as the user who made the request to the web application.

What am I doing wrong with the HttpClient implementation that is causing it to not pass the credentials correctly (or is it a bug with the HttpClient)?

The reason I want to use the HttpClient is that it has an async API that works well with Tasks, whereas the WebClient's asyc API needs to be handled with events.


Solution

  • I was also having this same problem. I developed a synchronous solution thanks to the research done by @tpeczek in the following SO article: Unable to authenticate to ASP.NET Web Api service with HttpClient

    My solution uses a WebClient, which as you correctly noted passes the credentials without issue. The reason HttpClient doesn't work is because of Windows security disabling the ability to create new threads under an impersonated account (see SO article above.) HttpClient creates new threads via the Task Factory thus causing the error. WebClient on the other hand, runs synchronously on the same thread thereby bypassing the rule and forwarding its credentials.

    Although the code works, the downside is that it will not work async.

    var wi = (System.Security.Principal.WindowsIdentity)HttpContext.Current.User.Identity;
    
    var wic = wi.Impersonate();
    try
    {
        var data = JsonConvert.SerializeObject(new
        {
            Property1 = 1,
            Property2 = "blah"
        });
    
        using (var client = new WebClient { UseDefaultCredentials = true })
        {
            client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
            client.UploadData("http://url/api/controller", "POST", Encoding.UTF8.GetBytes(data));
        }
    }
    catch (Exception exc)
    {
        // handle exception
    }
    finally
    {
        wic.Undo();
    }
    

    Note: Requires NuGet package: Newtonsoft.Json, which is the same JSON serializer WebAPI uses.