Chris Umbel

Basic Authentication with a NSURLRequest in Cocoa Touch

While working on a certain unnamed iPhone app lately I ran across the need to use basic authentication in communication with REST services. For something that seems to be such a fundamental need for mobile applications I figured most of the work would be done for me. Turns out that's not the case. A few details are left up to you, the Cocoa Touch programmer.

What Basic Authentication is

In order to implement basic authentication in Cocoa Touch it is important to understand how it works. Basic authentication tokens are essentially formatted into a string in the format:

username:password

They are then Base64-encoded and formatted into an "Authorization" HTTP header who's value looks like:

Basic c2NvdHQ6dGlnZXI=

where "c2NvdHQ6dGlnZXI=" is the encoded token pair and "Basic" is a hard-coded prependage.

Easy enough right? Encode username and password, slap it in the header and make the request.

What's not Provided by Cocoa Touch: Base64

I thought for sure I'd be able to leverage something out-of-the-box to handle the Base64 encoding. Surely I can do it by using some simple method or function somewhere. It'd just be a one-liner something-er-other, right? Right?? Wrong!

Nothing in Cocoa Touch natively provides you with Base64 encoding capabilities. You also don't have access to openssl on the iPhone via the SDK. In Cocoa Touch's defense I guess it never claimed to have the batteries included (my python & ruby soaked brain always expects everything to be done for me;)).

While I've seen suggestions to statically link openssl against your iPhone app it's not only overkill but presumably puts the responsibility on you to comply with U.S. export regulations (the cryptography in openssl is legally a munition in the states after all).

Besides, it's rather simple to implement Base64 yourself.

A Base64 Implementation

There are a number of implementations you can cherry-pick from elsewhere but in the spirit of demonstration I'll provide an example Base64 encoder that you can use in your project.

Contrary to the spirit of demonstration, however, I'm not going to explain it too much as it's not the subject of the post. If you need more background about the algorithm Ramkumar Menon wrote an excellent blog post about it. Also note that the code is arranged for readability, not conciseness, style or best-practices.

static char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

@implementation Base64
+(NSString *)encode:(NSData *)plainText {
	int encodedLength = (4 * (([plainText length] / 3) + (1 - (3 - ([plainText length] % 3)) / 3))) + 1;
	unsigned char *outputBuffer = malloc(encodedLength);
	unsigned char *inputBuffer = (unsigned char *)[plainText bytes];
	
	NSInteger i;
	NSInteger j = 0;
	int remain;
	
	for(i = 0; i < [plainText length]; i += 3) {
		remain = [plainText length] - i;
		
		outputBuffer[j++] = alphabet[(inputBuffer[i] & 0xFC) >> 2];
		outputBuffer[j++] = alphabet[((inputBuffer[i] & 0x03) << 4) | 
									 ((remain > 1) ? ((inputBuffer[i + 1] & 0xF0) >> 4): 0)];
		
		if(remain > 1)
			outputBuffer[j++] = alphabet[((inputBuffer[i + 1] & 0x0F) << 2)
										 | ((remain > 2) ? ((inputBuffer[i + 2] & 0xC0) >> 6) : 0)];
		else 
			outputBuffer[j++] = '=';
		
		if(remain > 2)
			outputBuffer[j++] = alphabet[inputBuffer[i + 2] & 0x3F];
		else
			outputBuffer[j++] = '=';			
	}
	
	outputBuffer[j] = 0;
	
	NSString *result = [NSString stringWithCString:outputBuffer length:strlen(outputBuffer)];
	free(outputBuffer);
	
	return result;
}
@end

Creating and Using a Proper Request

Now that we're ready to speak the encoding that the webservers are expecting we can get down to business. Consider the following code which executes an authenticated request against a resource via a synchronous NSURLRequest. Adding an "Authorization" header with the appropriately formatted, Base64-encoded authentication tokens are all that's required to authenticate the request.

NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/"];
NSString *userName = @"scott";
NSString *password = @"tiger";

NSError *myError = nil;

// create a plaintext string in the format username:password
NSMutableString *loginString = (NSMutableString*)[@"" stringByAppendingFormat:@"%@:%@", userName, password];

// employ the Base64 encoding above to encode the authentication tokens
NSString *encodedLoginData = [Base64 encode:[loginString dataUsingEncoding:NSUTF8StringEncoding]];
	
// create the contents of the header 
NSString *authHeader = [@"Basic " stringByAppendingFormat:@"%@", encodedLoginData];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url
    cachePolicy: NSURLRequestReloadIgnoringCacheData  
    timeoutInterval: 3];   

// add the header to the request.  Here's the $$$!!!
[request addValue:authHeader forHTTPHeaderField:@"Authorization"];

// perform the reqeust
NSURLResponse *response;

NSData *data = [NSURLConnection  
    sendSynchronousRequest: request  
    returningResponse: &response  
    error: &myError];  
*error = myError;

// POW, here's the content of the webserver's response.
NSString *result = [NSString stringWithCString:[data bytes] length:[data length]];

Conclusion

Aside from rolling-your-own (or snagging-someone-elses) Base64 implementation this isn't too bad.

To take it further you might employ NSURLCredential for storage of your authentication tokens.

Also if an asynchronous NSMutableURLRequest is used you can easily handle a webserver issuing a challenge by implementing the didReceiveAuthenticationChallenge message.

Sun Jan 24 2010 13:01:00 GMT+0000 (UTC)

8 Comments Comment Feed - Permalink
i tried this but there are some issues in code.
first one which one i resolved is
NSString userName = @\"scott\";  
NSString password = @\"tiger\";
userName and password are pointer type so \"*\" is missing. 2nd which i was unable to resolve is in Base64 outputBuffer is undeclared and use first time in this function. so please tell me how to resolve this problem. 
by Muhammad Usman Aleem on Sat Sep 04 2010 22:59:00 GMT+0000 (UTC)
ah, thanks for pointing those out.  i've updated both sections of code to address the issues you pointed out.
by chrisumbel on Sat Sep 04 2010 22:59:44 GMT+0000 (UTC)
Great article.  Thank you for posting.
by N.S. on Tue Feb 07 2012 02:55:48 GMT+0000 (UTC)
Thanks for the useful post. Here are a few tips:

A more standard (simpler) way of specifying the login string is:

NSString *loginString = [NSString stringWithFormat:@"%@:%@", userName, password];

Also, the "result" line uses a deprecated method. This is better:

NSString *result = [NSString stringWithUTF8String:[data bytes]];
by Demitri Muna on Sun Jan 29 2012 22:17:18 GMT+0000 (UTC)
very informative  , thanks for the post
by Ahmad on Wed Feb 29 2012 14:21:59 GMT+0000 (UTC)
Nice article 
 
by vimal on Mon Apr 23 2012 05:24:38 GMT+0000 (UTC)
Thanks for share
by Diddy on Fri Jun 15 2012 04:33:54 GMT+0000 (UTC)
Thanks for the article!
by Jeroen on Fri Jan 10 2014 13:14:59 GMT+0000 (UTC)
Add a comment
Name
E mail (Private)
URL
Follow Chris
RSS Feed
Twitter
Facebook
CodePlex
github
LinkedIn
Google