Authentication with NSURLConnection sendAsynchronousRequest with completion handler

Nick picture Nick · Oct 10, 2012 · Viewed 17.2k times · Source

Generally I like to just "fire and forget" with NSURL's sendAsynchronousRequest class method using the completion handler block but it seems that might not be an option when authentication is needed.

When using a completion handler style request like this:

[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.mysite.com/"]]
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {


                            //Do Stuff   

                           }];

What is the proper way to handle authentication? Do I need to alloc and init the NSURLConnection and set a delegate instead of doing using this class method style? I think I understand how to authenticate correctly with the delegate function but I'm trying to figure out if I can include that in the completionHandler block or if there is a better way to do this.

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{

    if ([challenge previousFailureCount] > 0) {
        NSLog(@"Authentication Failure");
        [connection cancel];
    }
    else
    {

        NSURLCredential *credential = [NSURLCredential credentialWithUser:self.username
                                                                 password:self.password
                                                              persistence:NSURLCredentialPersistenceForSession];
        [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    }

}

Answer

Darren picture Darren · Oct 16, 2012

I think the completionHandler method is for basic requests. Maybe you could consider using AFNetworking as I use this with block methods and authentication.

EDIT.... Have you tried adding the authentication header to the NSURLRequest? Create an NSMutableURLRequest:

NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.example.com/"]];

And add the authentication header like this:

NSString *basicAuthCredentials = [NSString stringWithFormat:@"%@:%@", userName, password];
NSString *authValue = [NSString stringWithFormat:@"Basic %@", AFBase64EncodedStringFromString(basicAuthCredentials)];
[urlRequest setValue:authValue forHTTPHeaderField:@"Authorization"];

The AFBase64EncodedStringFromString function is this:

static NSString * AFBase64EncodedStringFromString(NSString *string) {
    NSData *data = [NSData dataWithBytes:[string UTF8String] length:[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
    NSUInteger length = [data length];
    NSMutableData *mutableData = [NSMutableData dataWithLength:((length + 2) / 3) * 4];

    uint8_t *input = (uint8_t *)[data bytes];
    uint8_t *output = (uint8_t *)[mutableData mutableBytes];

    for (NSUInteger i = 0; i < length; i += 3) {
        NSUInteger value = 0;
        for (NSUInteger j = i; j < (i + 3); j++) {
            value <<= 8;
            if (j < length) {
                value |= (0xFF & input[j]);
            }
        }

        static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

        NSUInteger idx = (i / 3) * 4;
        output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F];
        output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F];
        output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6)  & 0x3F] : '=';
        output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0)  & 0x3F] : '=';
    }

    return [[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding];
}

Then call the function you called before, but using your new NSURLRequest:

[NSURLConnection sendAsynchronousRequest:urlRequest
  queue:[NSOperationQueue mainQueue]
  completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

}];