How to 'share' NTLM authentication across multiple HttpWebRequests?
Asked Answered
U

2

8

My C# app hits a web server that uses NTLM authentication.

I find that each request made to the server (using a new HttpWebRequest) is individually authenticated. In other words, every request results in a 401 response, after which an NTLM handshaking conversation occurs before I then get the actual response.

e.g.:

First GET request:

-> GET xyz 
<- 401 error (WWW-Authenticate:NTLM)

-> GET xyz (Authorization:NTLM base64stuff)
<- 401 error (WWW-Authenticate:NTLM base64stuff)

-> GET xyz (Authorization: base64stuff)
<- 200

Subsequent requests:

-> GET xyz (Authorization:NTLM base64stuff)
<- 401 error (WWW-Authenticate:NTLM) //can this request be avoided?

-> GET xyz (Authorization: base64stuff)
<- 200

(initially, with PreAuthenticate set to false, the subsequent requests looked like the first request - i.e. three underlying requests per 'request')

Is there a way to 'share' the authentication performed on the first request to the server with subsequent HttpWebRequests?

I thought perhaps the UnsafeAuthenticatedConnectionSharing property would allow me to do this, but setting it to true for all HttpWebRequest objects used in the app has no effect.

However if I set PreAuthenticate to true, one less 401 response happens for each request after the first one.

Underset answered 31/8, 2011 at 11:17 Comment(8)
What problem are you trying to solve?Alkahest
I want to effectively 'do authentication' once, and reuse this across HttpWebRequests. Rather than authenticating each time I hit the server. I may be misunderstanding NTLM though, and in fact NTLM imposes an overhead of an extra request on every request made.Underset
Yes but what is the problem with the way it works now, what problem would it solve to change the behavior you describe to the behavior you desire, what are you trying to achieve in the real world.Alkahest
The problem with the way it was initially working was that every request to the server resulted in 3 underlying HTTP request/responses. With PreAuthenticate set to True, that drops to 2. But can the app be 'fixed' so that just one request/response is made per HTTP request (apart from the initial request of course)? I want to avoid the network traffic overhead involved in two-requests-per-HTTP-request, if possible.Underset
@Ben: Each additional request means one round-trip to the server. Although it doesn't mean too much traffic, it contributes to slowing the speed of each request - the duration between sending the request and having the response ready is almost doubled, even in fast connections.Therefor
@Ben in my opinion, tens of milliseconds is a very, very long time! Yes, there are many un-needed optimizations that programmers tend to do in their code, that may save a few clock cycles. But consider how much computation can take place in today's CPUs in each millisecond. 50ms of time is quite noticable by the user, and optimizing them is what makes a software snappy. Plus, if through put is important (you're making many requests sequentially), this will double the requests / second. So, basically I agree with you, but it's subject to the usecase's requirements.Therefor
@Ben IMO a server round-trip is worth optimising away, if possible. I wouldn't call it a micro-optimisation. However, if I can't find a solution - it's not the end of the world!Underset
BTW, for me the overhead of each request is 150ms. meaning that one request that ends up being 3 requests takes up getting on for half a second.Underset
T
4

Last request sent after NTLM is performed (the one that results in a 200 response) contains an auth header that tells the server that you have the correct credentials.

I'm not sure if the client class has the feature to keep this by its own, but if you find some way to retain this header and add it to your subsequent requests it should work fine.


Update: NTLM authenticates a connection, so you need to keep your connection open using Keep-Alive header. The client class should provide some settings for this.

Therefor answered 31/8, 2011 at 11:55 Comment(4)
Hmmm. It is possible to set the Authorization header on an HttpWebRequest, and I tried that. However it didn't work - the server returns a 400 Bad Request, indicating that the Authorization header can't be 'replayed' in this way. Which I suppose makes sense for security reasons.Underset
After doing a wee bit of testing, it looks like, although HttpWebRequest has a KeepAlive property, that it doesn't improve things over and above setting PreAuthenticate=true. A bit annoying.Underset
@mackenir: Did you solve this problem? I am also seeing the exact same issue as you have described above. Setting PreAuthenticate and KeepAlive to true doesn't seem to help.Microtone
@SureshKumar: see the answer below from marcofo88, that did the trick for me.Gamma
L
3

Maybe it is a bit too late for it, but you have to set the UnsafeAuthenticatedConnectionSharing property to true in the WebRequestHandler (it extends HttpClientHandler).

This way connections are kept alive by allowing the HttpClient to "share" the authentication among other requests, allowing at the same time the connections to be kept alive (you can't do this manually even by setting the header yourself). Bear in mind that you should also have the appropriate persistent authorization in the server, either with authPersistNonNTLM for Kerberos or with authPersistSingleRequest for NTLM.

Lenrow answered 20/8, 2018 at 13:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.