anaisbetts / ModernHttpClient

HttpClient implementations that use platform-native HTTP clients for :rocket:
MIT License
658 stars 260 forks source link

Authentication using NTLM #170

Open kubal5003 opened 9 years ago

kubal5003 commented 9 years ago

Hello, I'm trying to figure out if using NTLM with ModernHttpClient is actually possible. After examining the code I made a conclusion that it is not currently possible. The problem seems to be here:

public override void DidReceiveChallenge(....

Inside that method another if statement would be needed - something like this:

 if (challenge.ProtectionSpace.AuthenticationMethod == NSUrlProtectionSpace.AuthenticationMethodNTLM) {

And I would have to pass NSUrlCredential object to completion handler.

completionHandler(NSUrlSessionAuthChallengeDisposition.UseCredential, new NSUrlCredential(cred.Domain + "\\" + cred.UserName, cred.Password, NSUrlCredentialPersistence.ForSession));

The tricky part would be to figure out where to get those credentials from. My first thought was to use the Credentials object that is supposed to implement interface ICredentials. The default NetworkCredentials class most probably has everything that is needed. It is already being used for the same purpose in the default implementation from System.Net namespace. To me that means that:

Now the list of questions: Is there any simpler method to use NTLM ? Did I get this right? Would you accept a PR if I did implement this?

TheAlmightyBob commented 9 years ago

Great to see this, but a problem with this first implementation is that it can just keep retrying with wrong credentials. I'm not an expert here, but it looks like it should be paying attention to challenge.PreviousFailureCount and bailing if it's already failed (let the caller handler the 401 response at that point, prompt the user for new credentials, etc).

A quick googling turned up the following Objective-C example that handled previousFailureCount: https://groups.google.com/forum/#!topic/phx-ios-developer-group/Vb6xSo3be3A

(in case that link doesn't last, the gist is that if previousFailureCount > 0, then it calls completionHandler with NSUrlSessionAuthChallengeDisposition.CancelAuthenticationChallenge)

(should I have made this a new issue or a comment on the pull request instead?)

kubal5003 commented 9 years ago

Wow, thanks for this hint!!! I did observe this behavior - it time outs after 30 seconds. I didn't know what was happening and had no way of actually debugging this code to see it live. I guess the classic way of debugging (reading) proved to be a lot better in this case :) I'll fix it. I can't promise any dates now, but should be fixed within a month at most.

TheAlmightyBob commented 9 years ago

I'm not sure what your debugging limitations were, but if you proxy it through something like Fiddler you'll see a big stream of 401s in that case.

TheAlmightyBob commented 9 years ago

I took a look at this, and it's not quite the simple quick fix I'd hoped. Using CancelAuthenticationChallenge does indeed cancel, but the caller winds up just receiving an OperationCanceledException rather than a 401 (or UnauthorizedAccessException, but I think a 401 is more expected). Not a good indicator that they may need to ask the user to re-enter credentials.

DidReceiveChallenge could theoretically just complete the response directly in this case... I'm not sure if it's essential to call the completionHandler? (does skipping that leave the NSUrlSessionTask alive?)

The mapping from UserCancelledAuthentication to OperationCanceledException happens in createExceptionForNSError, but I assume there is a reason for that. I don't know what other scenarios result in a UserCancelledAuthentication error.