Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSURLConnection does not use the default credential

I'm trying to connect to http://cmis.demo.nuxeo.org/nuxeo/atom/cmis/ with NSURLConnection. This demo web service is documented to require authentication (login: Administrator / password: Administrator).

Edit: This web service now sends an authentication challenge, it was not at the time the question was asked.

This web service does not send an authentication challenge, so I can't use the connection:didReceiveAuthenticationChallenge: delegate method. Instead I set a default NSURLCredential in the shared credential storage. Unfortunately, this default credential is not used by NSURLConnection.

Here is my code (using ARC, tested on iOS 5):

@implementation ViewController
{
    NSMutableData *responseData;
}

- (IBAction) connect:(id)sender
{
    NSString *user = @"Administrator";
    NSString *password = @"Administrator";
    NSURL *nuxeoURL = [NSURL URLWithString:@"http://cmis.demo.nuxeo.org/nuxeo/atom/cmis/"];

    NSURLCredential *credential = [NSURLCredential credentialWithUser:user password:password persistence:NSURLCredentialPersistenceForSession];
    NSString *host = [nuxeoURL host];
    NSNumber *port = [nuxeoURL port];
    NSString *protocol = [nuxeoURL scheme];
    NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:host port:[port integerValue] protocol:protocol realm:nil authenticationMethod:NSURLAuthenticationMethodHTTPBasic];
    [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:nuxeoURL];
    BOOL manuallyAddAuthorizationHeader = NO;
    if (manuallyAddAuthorizationHeader)
    {
        NSData *authoritazion = [[NSString stringWithFormat:@"%@:%@", user, password] dataUsingEncoding:NSUTF8StringEncoding];
        NSString *basic = [NSString stringWithFormat:@"Basic %@", [authoritazion performSelector:@selector(base64Encoding)]];
        [request setValue:basic forHTTPHeaderField:@"Authorization"];
    }
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    [connection start];
}

- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    responseData = [NSMutableData data];
}

- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [responseData appendData:data];
}

- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"connectionDidFinishLoading:%@", connection);
    NSLog(@"%@", [[NSString alloc] initWithData:responseData encoding:NSISOLatin1StringEncoding]);
}

- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"connection:%@ didFailWithError:%@", connection, error);
}

@end

Since the default credential is not used, I receive html (login page) instead of xml. If I set the manuallyAddAuthorizationHeader variable to YES, then authorization works and I receive xml.

My question is: why does NSURLConnection does not automatically use the default NSURLCredential?

like image 746
0xced Avatar asked Dec 21 '22 01:12

0xced


1 Answers

Sending the default credential without an authentication challenge is considered insecure, especially in the case where you're communicating over plain HTTP. The HTTP spec says that you can send credentials proactively, but that you should usually wait for an authentication challenge. And that's the behavior that Cocoa provides by default.

If you need to proactively send the credentials, then you'll have to manually add the headers yourself. You can just base-64 encode it yourself, or you can use the CFHTTP functions to do it for you, like so:

CFHTTPMessageRef dummyRequest =
    CFHTTPMessageCreateRequest(
        kCFAllocatorDefault,
        CFSTR("GET"),
        (CFURLRef)[urlRequest URL],
        kCFHTTPVersion1_1);
CFHTTPMessageAddAuthentication(
    dummyRequest,
    nil,
    (CFStringRef)username,
    (CFStringRef)password,
    kCFHTTPAuthenticationSchemeBasic,
    FALSE);
authorizationString =
    (NSString *)CFHTTPMessageCopyHeaderFieldValue(
        dummyRequest,
        CFSTR("Authorization"));
CFRelease(dummyRequest);

Then just set the authorizationString as the Authorization header in your NSURLRequest.

(Code copied blatantly from https://stackoverflow.com/a/509480/100478, though I've written similar code myself many times.)

like image 64
BJ Homer Avatar answered Jan 06 '23 22:01

BJ Homer