15

I'm trying to communicate with a self-hosted WebAPI client using HttpClient. The client is created with the following code:

HttpClientHandler clientHandler = new HttpClientHandler()
{
    UseDefaultCredentials = true,
    PreAuthenticate = true
};
var client = new HttpClient(clientHandler);

on the server side we set:

HttpListener listener = (HttpListener)app.Properties[typeof(HttpListener).FullName];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;

in the Startup file.

The problem is that I get two (or one after preauthentication) 401 errors before the request is processed.

In fiddler the sequence looks like this:

First request:
Authorization: Negotiate TlRMTVNTUAABAAAAl7II4gcABwAxAAAACQAJACgAAAAGAbEdAAAAD1dTMTEzLTEyMFNXVC0xMTM=
Answer:
WWW-Authenticate: Negotiate TlRMTVNTUAACAAAADgAOADgAAAAVwonisrQOBMTKHhKwCkgCAAAAAJoAmgBGAAAABgGxHQAAAA9TAFcAVAAtADEAMQAzAAIADgBTAFcAVAAtADEAMQAzAAEAEgBXAFMAMQAxADMALQAxADIAMAAEABYAcwB3AHQALQAxADEAMwAuAGwAbwBjAAMAKgBXAFMAMQAxADMALQAxADIAMAAuAHMAdwB0AC0AMQAxADMALgBsAG8AYwAFABYAQQBMAEQASQA5ADkAOQAuAGwAbwBjAAcACACkGh0XVY3QAQAAAAA=

Second request (succeeds):
Authorization: Negotiate TlRMTVNTUAADAAAAAAAAAFgAAAAAAAAAWAAAAAAAAABYAAAAAAAAAFgAAAAAAAAAWAAAAAAAAABYAAAAFcKI4gYBsR0AAAAPfJafWSuLL0sAXYtWCynOqg==

So why isn't my client sending the correct authorization token the first time around but always needs this two-time approach?

Voo
  • 29,040
  • 11
  • 82
  • 156
  • Please share you Authentication Handler – Dave Alperovich May 19 '15 at 02:26
  • Possible duplicate of http://stackoverflow.com/questions/24021413/can-i-make-httpwebrequest-include-windows-credentials-without-waiting-for-a-401 – Mike May 20 '15 at 18:04
  • You've talked about two 401s, but then you've only shown one failed request. Could you show a complete example of request/response (fail), request/response (fail), request/response (success)? – Jon Skeet May 21 '15 at 06:00
  • @Mike, the questions speak to similar issues but are not at all the same. I have yet to see Voo's Authentication Handler, but this issue deals with Self-Hosted. The post you pointed to involves Windows AD and what I assume is standard .Net authentication framework with standard configuration. – Dave Alperovich May 21 '15 at 07:42

3 Answers3

17

What you are experiencing is normal, this is how the NTLM authentication scheme works.

1: C  --> S   GET ...

2: C <--  S   401 Unauthorized
              WWW-Authenticate: NTLM

3: C  --> S   GET ...
              Authorization: NTLM <base64-encoded type-1-message>

4: C <--  S   401 Unauthorized
              WWW-Authenticate: NTLM <base64-encoded type-2-message>

5: C  --> S   GET ...
              Authorization: NTLM <base64-encoded type-3-message>

6: C <--  S   200 Ok
  1. The client sends a GET request to the server.
  2. Since you need to be authenticated to access the requested resource, the server sends back a 401 Unathorized response and notifies the client in the WWW-Authenticate header that it supports NTLM authentication. So this is where you get your first 401 response code.
  3. The client sends the domain name and the username to the server in the Authorization header. Note that based solely on these information the client cannot be authenticated yet.
  4. The server sends a challenge to the client. It's a randomly generated number called a nonce. This is where you get your second 401 response code.
  5. The client sends back a response to the server's challenge, using its password's hash to encrypt the random number.
  6. The server sends the client's username, the challenge sent to the client and the response received from the client to the domain controller. Using the username the domain controller retrieves the hash of the user's password and encrypts the challenge with it. If the result matches the response sent by the client, the client is authenticated and the server sends back a 200 response code and the requested resource to the client.
zspalotai
  • 276
  • 1
  • 4
5

Faced a similar issue, and after going through a lot of answers, where none of those worked. The following worked, and it doesn't come up with two 401's:

var credential = new NetworkCredential("username", "password", "domainname");
var myCache = new CredentialCache();

// Add the target Uri to the CredentialCache with credential object
myCache.Add(new Uri("http://targeturi/"), "NTLM", credential);

// Create an HttpClientHandler to add some settings
var handler = new HttpClientHandler();
handler.AllowAutoRedirect = true;
handler.Credentials = myCache;

// Create an HttpClient with the handler object
httpClient = new HttpClient(handler);

// Wait to get the reponse, and you can use the reponse in your code
HttpResponseMessage response = await httpClient.GetAsync(resourceUri);

Source

Yahya
  • 3,386
  • 3
  • 22
  • 40
  • I get an exception with your solution on "handler.Credentials = myCache;": The value 'System.Net.CredentialCache' is not supported for property 'Credentials'. Any idea? – Gold.strike Oct 21 '16 at 16:41
  • @Gold.strike you can just set handler.Credentials = credential; The credential cache is not needed. – Dmitry Pavlov May 15 '17 at 21:20
0

the default behavior where it only sends credentials after receiving an HTTP 401 Not Authorized response.

Manually adding the credentials header seems to be the best solution available.

More details in this post

silver
  • 1,633
  • 1
  • 20
  • 32
  • I know that, but that still shouldn't result in *two* 401s before the 200. And you can't add credentials yourself when using Windows authentication can you? – Voo May 17 '15 at 16:58
  • I think you can, see at the bottom of post. req.Credentials = new NetworkCredential(user, pwd, domain); – silver May 18 '15 at 07:18
  • Sure the user I could get, the domain as well, but obviously I have no way to obtain the password (big security flaw that would be) - that only works via `DefaultCredentials`. And in any case that all looks like basic authentication to me there, not NTLM – Voo May 18 '15 at 07:23