Lessons From iOS: HTTP authentication

I recently became the maintainer of a universal iOS application. The application was designed and built by a student at LeTourneau University. The App is called iLETU. It is a shortcut to the dozens of student related websites and resources that a LeTourneau Student might need to access. One of the most convenient features of the app is an automatic login to a number of student websites. Each of the five websites accessible from the app via auto login require different login methods. After spending time reading the devDocs, searching Stack Overflow, and trying out a few ideas in XCode I think I finally understand how to do HTTP basic authentication on iOS and OS X.

The secret to HTTP basic authentication using cocoa is knowing NSURL and the related classes. 

  • NSURL
  • NSURLRequest/NSMutableURLRequest
  • NSURLConnection
  • NSURLCredential
  • NSURLCredentialStorage
  • NSURLProtectionSpace
  • UIWebView/WebView/NIWebController etc.

If your project actually wants to display web content to the user the no brainier solution is to use Apple's UIWebView(Or WebView if you are on a Mac). For the iLETU app some basic navigation was also necessary so I used NIWebController, a handy view controller for web views from the nimbus framework that adds a toolbar to the basic WebView.

Webviews load data from a NSURLRequest object. NSURLRequests provide basic information for requesting a URL: timeout, HTTP fields, etc. Websites are requested via HTTP GET requests. Most forms on websites are submitted via a HTTP POST request.

But the real magic comes from NSURLConnection. In the words of the devDocs, "An NSURLConnection object provides support to perform the loading of a URL request." If you want to load some a URL in the background without displaying it you would use NSURLConnection. The real power of the NSURLConnection is in the method

+ (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id < NSURLConnectionDelegate >)delegate

The NSURLConnectionDelegate protocol has methods for responding to successful connections, fatal errors, and authentication challenges. If you are trying to access data Protected by HTTP basic authentication this is how Cocoa does it. At this point an example should bring some clarity.  

//basic HTTP authentication
NSURL *url = [NSURL URLWithString: urlString];
NSMutableURLRequest *request;
request = [NSMutableURLRequest requestWithURL:url
                                  cachePolicy:NSURLRequestReloadIgnoringCacheData
                              timeoutInterval:12];
[self.webView openRequest:request];
(void)[NSURLConnection connectionWithRequest:request delegate:self];

This creates a URL. From the URL a URLRequest is created. The URLRequest is then loaded in the web view. The Request is also used to make a URLConnection. We don't really use the connection, but we need to receive notifications about authentication so we set the delegate. There are only two methods we need from the delegate.

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{   
    NSURLCredential * cred = [NSURLCredential credentialWithUser:@"username"
                                                        password:@"password"
                                                     persistence:NSURLCredentialPersistenceForSession];
    [[NSURLCredentialStorage sharedCredentialStorage]setCredential:cred forProtectionSpace:[challenge protectionSpace]];
    
}

- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
{
    return YES;
}

Whenever there is an authentication challenge a credential is added to the credential storage. You also tell the connection to use the credential storage.