3

I have an iOS app which logs-in a user and then authenticates that user with an appKey. The didReceiveAuthenticationChallenge delegate method on NSURLConnection is sufficient for providing a username / password with an NSURLCredential. However, I'm having trouble creating an NSURLCredential with only an appKey (just one piece of auth data, not two). Is there anyway to provide authentication details to a server with NSURLConnection with only a key?

The following curl request works perfectly by only providing the appKey, no password:

curl -u <app key>: -H "Accept: application/json" https://dev.sitename.com/api/v1/webpage -v

This is how I've translated the above cURL authentication part to Objective-C:

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    // Authenticate the user with AppKey based on the credentials provided
    if ([challenge previousFailureCount] == 0) {
        NSURLCredential *credential = [NSURLCredential credentialWithUser:[keychainItem objectForKey:@"APP KEY"] password:nil persistence:NSURLCredentialPersistenceForSession];
        [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    }
}

This should work, but every time I try it the authentication fails. As you can see I've set the password parameter to nil. I've also tried filling in both parameters. Nothing seems to work.

Any ideas on how I could only pass one parameter to the server using NSURLConnection? Is there any way to get error information from the didReceiveAuthenticationChallenge delegate method (I'm aware of didReceiveResponse)?

Guillaume Algis
  • 10,705
  • 6
  • 44
  • 72
Sam Spencer
  • 8,492
  • 12
  • 76
  • 133

1 Answers1

2

This seems to be an undocumented behavior of NSURLCredential: when passing nil as a password to credentialWithUser:password:persistence:, and using this credential to respond the authentication challenge, iOS ignore the credential altogether.

The solution is to replace the nil by an empty NSString (@"") when you don't want to use a password.

So your code should look something like:

NSURLCredential *credential = [NSURLCredential credentialWithUser:[keychainItem objectForKey:@"APP KEY"] password:@"" persistence:NSURLCredentialPersistenceForSession];

This is what I get from my nginx's logs with a nil password:

xxx.xxx.xxx.xxx - - [18/Aug/2013:23:21:14 +0200] "GET /tmp HTTP/1.1" 401 188 "-" "test/1.0 CFNetwork/609.1.4 Darwin/12.4.0"

And with an empty string (apikey being the username used in response to the challenge):

xxx.xxx.xxx.xxx - apikey [18/Aug/2013:23:21:16 +0200] "GET /tmp HTTP/1.1" 200 136 "-" "test/1.0 CFNetwork/609.1.4 Darwin/12.4.0"
Guillaume Algis
  • 10,705
  • 6
  • 44
  • 72
  • While reading your question again I saw the "I've also tried filling in both parameters. Nothing seems to work" bit. Did you already try the `@""` ? – Guillaume Algis Aug 18 '13 at 21:39
  • Thanks for the answer! I tried that, but it didn't work. Also, I believe that `@""` is equivalent to `nil`. – Sam Spencer Aug 19 '13 at 02:48
  • 1
    Sorry this didn't work. And no, [`nil` is not the same as `@""`](http://stackoverflow.com/questions/4283690/objective-c-whats-the-difference-between-null-nil-and). Have you tried logging the HTTP response sent by your app (with [Charles](http://www.charlesproxy.com/) or something similar) to see whether its a client or server problem ? – Guillaume Algis Aug 19 '13 at 09:50
  • Thanks for the info - thats good to know. And it's a good thing you provided that link because the KeychainItem was being retrieved from an `NSDictionary`, and `NSURLCredential` was having some issues getting the object from the dictionary. In combination with your `@""` trick, this worked! – Sam Spencer Aug 20 '13 at 01:26