2

I have an MVC Application and an associated Web API project that are both hosted on a remote server on IIS. They share the same application pool. Whenever I try to make a call to the Web API from the MVC Application I get a 403 error, which appears to be coming from bad credentials being passed by the HttpClientHandler. I have

UseDefaultCredentials = true 

and I have tried setting

Credentials = CredentialCache.DefaultNetworkCredentials

but neither of these allows the API request to go through.

Setting the Application Pool to use my AD Username/Password allows all API requests to go through, and also calling the API directly from Postman returns data properly.

My assumption is that IIS AppPool[Pool Name] is getting forwarded in the request, and the proper credentials are never passed. Is there anyway around this without making the API unsecure (I.e. only a couple of domain groups should be able to access it)?

Example of a call I'm making to the API from the MVC application

    public async Task<HttpResponseMessage> CreateIncident(Incident model)
    {
        using (var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }))
        {
            var newIncident = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json");
            var response = await client.PostAsync(hostUri, newIncident);
            return response;
        }
    }
Tseng
  • 61,549
  • 15
  • 193
  • 205
Robert McCoy
  • 661
  • 1
  • 10
  • 19
  • Are you going through a proxy at all? – joshmcode Feb 28 '17 at 00:30
  • No, this is all hosted on an internal server and all calls are going exclusively through that server. There is no proxy. Like I said, Postman and direct calls to the API from my computer go through, but it appears .NET Core can't impersonate the requests, and I was kind of hoping someone else has hit this recently that can give a more up to date solution than some of the 6+ month old ones that are a bit outdated in .NET Core. – Robert McCoy Feb 28 '17 at 00:35

3 Answers3

3

Without more information it is hard to say for sure, but the issue is likely due to the double-hop authentication you are attempting.

  1. the client application (browser in the case of a website) authenticates the user the client sends
  2. the authentication to the server (MVC application)
  3. the MVC application then tries to pass the authentication along to the web service

When I needed to do a similar task, I was unable to get HttpClient to work. I tried a number of suggested solutions from this question, How to get HttpClient to pass credentials along with the request?. While it was informative -- specifically, this portion of BlackSpy's answer explained why:

What you are trying to do is get NTLM to forward the identity on to the next server, which it cannot do - it can only do impersonation which only gives you access to local resources.

I ended up using WebClient (with required targeting .NET framework) with something like this in the MVC application (downloading a file from the web api, in this case):

private async Task GetFileAsync(Identity identity, string serviceAddress, Stream stream)
{
    var windowsIdentity = Identity as WindowsIdentity;

    if (windowsIdentity == null)
    {
        throw new InvalidOperationException("Identity not a valid windows identity.");
    }

    using (windowsIdentity.Impersonate())
    {
        using (var client = new WebClient { UseDefaultCredentials = true })
        {
            var fileData = await client.DownloadDataTaskAsync(serviceAddress);
            await stream.WriteAsync(fileData, 0, fileData.Length);
            stream.Seek(0, SeekOrigin.Begin);
        }
    }
}

While the requirement to target the full framework prevents this from being a .NET Core solution, but it looks like it has been added since then.

Add WebClient to new System.Net.WebClient contract

This PR ports System.Net.WebClient to corefx. The code is mostly taken from desktop and then cleaned up a bit stylistically. The only major code rewrite was removing hundreds of lines of complicated APM callback-based code and replacing it with a few core async methods. There's still plenty of more cleanup that can be done, but functionally this is sufficient.

Community
  • 1
  • 1
David Culp
  • 5,354
  • 3
  • 24
  • 32
  • I saw this solution (and read far too many things about the double hop), but I don't think Web Client is in Core yet. They merged it into the repository, but as far as I can tell it was never included in any RC or Preview, and I can't reference it in Visual Studio. I was hoping someone would have a more up-to-date implementation for impersonation, as another Stack Overflow thread that I read had an outdated solution that no longer worked. I'd hate to point back to .NET Framework, but that's what this might come down too. Thanks! – Robert McCoy Feb 28 '17 at 00:33
  • Hey David, I finally found a solution that I've been looking for (Yea...it took a little while). You can find the GitHub issue discussion here, and the user ilanc has posted a link to an example repo way at the bottom of the discussion. https://github.com/aspnet/Home/issues/1805 – Robert McCoy Mar 01 '17 at 21:19
1

Without seeing the code its hard to say. Have you tried without setting the Credentials parameter? I would run fiddler and compare the request sent by the MVC application and Postman.

Shane Ray
  • 1,449
  • 1
  • 13
  • 18
  • I looked at the results from Fiddler. Postman isn't even doing any negotiation because of the Integrated Windows Authentication, the server is automatically determining my login credentials. As for the MVC application, it's sending Negotiation Authorization with the request, and that's returning a 401. That's why I'm thinking the API is getting the IIS AppPool information and sending that as the credentials. I also threw a bit of example code in the question. – Robert McCoy Feb 28 '17 at 00:05
  • Have you tried removing UseDefaultCredentials = true? – Shane Ray Feb 28 '17 at 01:45
  • Yea, it didn't seem to have any impact at all. Also tried doing PreAuth = false and Credentials set to something. Might just go with David's solution and have the app not be completely Core. – Robert McCoy Feb 28 '17 at 03:32
0

You can find out how to impersonate an already authenticated (windows) user at this GitHub discussion page: https://github.com/aspnet/Home/issues/1805

ilanc has a really good demo with a link towards the bottom.

Robert McCoy
  • 661
  • 1
  • 10
  • 19