In this RESTful world, access to any modern data-rich website is restricted with some kind of authentication mechanism. Several known open authentication mechanisms used on Web are HTTP Basic (RFC 1945) and HTTP Digest authentication (RFC 2617). While discussing Basic or Digest itself is a separate blog post, or a lengthy boring Wikipedia article, coding it in Objective C is not. No, I’m not talking about ASIHTTPRequest. While ASI is a great wrapper around CFNetwork, it’s always better to know what’s happening behind the scenes. So, let’s get started.

Mechanism of HTTP Authentication

When a client requests a server for a resource, it issues a GET request followed by the filename. The server checks if the resource is protected and if it is, it sends a “401 Unauthorized” response back with WWW-Authenticate in the header. The WWW-Authenticate header field might contain a value, which is either “Basic” or “Digest” depending on the mechanism.
When the client receives this authentication challenge, it is required to send the user authentication information in a specific format. For basic authentication, this is usually “base64(username:password)” in the Authorization header field. In “Digest” authentication request, it’s a bit more complicated. The client has to return much more sophisticated values in the Authorization header as requested by the server. Good thing is that, as Objective C developers, Core foundation classes have encapsulated all of those.

Doing authentication the CFNetwork way

The Core foundation classes has a handy helper function to retrieve content over HTTP. In most cases, we would be using [NSURLConnection sendSynchronousRequest:returningResponse:] class method. However, as explained in the documentation, this method doesn’t automatically help in authentication, unless the authentication parameters are provided as a part of the URL request. This means while accessing a resource, you should provide the authentication parameters in the URL request itself. While this is possible when your server uses basic authentication, it doesn’t help with digest authentication. The digest authentication protocol sends you information like nonce, realm and the client is support to take a MD5 hash of these values and send it along with the next request along with user’s login information. This means, you ought to make two requests to successfully access the resource, which is clearly impossible with a single synchronous request. Here comes NSURLConnection’s asynchronous handlers and delegates.
Apple’s core foundation network classes are so powerful that, you don’t have to know anything about the underlying “digest authentication” mechanism. As in any normal case, you create a NSURLConnection subclass and initialize it with a URL and wait for progress (or error) on the callback delegates,

-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    //probably append the received data in a member variable
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    //check the error domain and report errors back to the calling function via a delegate
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    //perform any cleanup and notify the delegate of available data
}

Authentication related delegates

In case the URL you accessed requires authentication, NSURLConnection sends you two more delegate callbacks as below

-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    //return YES to say that we have the necessary credentials to access the requested resource
    return YES;
}
 
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    //some code here, continue reading to find out what comes here 
}

The two authentication related delegates will be called when the server sends a WWW-Authenticate header.
When you get the first callback delegate, namely, canAuthenticateAgainstProtectionSpace, your class should return YES, if you know the credentials, else, you can request the user to enter them here. The second delegate callback will be called only when you return YES here. On the second delegate call, you get a pointer to a NSURLAuthenticationChallenge. You write a couple of lines of code here to set the username and password as expected by the server.

Using the NSURLCredential to authenticate with your web server

The CFNetwork framework provides a class called NSURLCredential which takes out the bulk of authentication related code. Just write the following couple of lines in the didReceiveAuthenticationChallenge: delegate and you are all set.

	NSURLCredential *credential = [NSURLCredential credentialWithUser:userName
                                                             password:userPassword]
                                                          persistence:NSURLCredentialPersistenceForSession];
 
	[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
}

In case your server requires you to encrypt the password, you can do so here before creating a credential token.

Now, if your username and password is correct, the server should no longer respond you with a 401, and you should get the necessary protected data on your usual didReceiveData delegate

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

So, by just implementing two delegates, you can now access authenticated information from a server. It’s also interesting to note that this delegate is agnostic of the underlying authentication mechanism. For example, if the server was originally requesting a “Basic” authentication token and then later on moved to “Digest” authentication, as a iPhone developer, you don’t have to worry about changing your code. The exact same code should work.

With this, we have come to the end of this post and I’ll reserve NTLM and Kerberos authentication using CFNetwork to another blog post.

Thanks for reading.


Mugunth

Follow me on Twitter