Question

From this question I've implemented uploading a file to a server: Upload File to Server

I was reading the Apple Documentation to find out about how secure the NSURLConnection sendSynchronousRequest method is, and the documentation says that it will use credentials in the URL if the file need authentication. My question is, How do I require authentication? I assume it's on the server side.

Was it helpful?

Solution

There are a tons of different types of authentication. For more information, see the Authentication Challenges and TLS Chain Validation section of the URL Loading System Programming Guide.

But, in answer to your question, yes, you probably want to employ security on your web server, and then have your iOS network requests authenticate using the appropriate credentials. If you're just dipping your toe into the world of authenticated requests, you should probably start with HTTP "Basic" authentication. (There are many other types of authentication, but going through all of them is beyond the scope of a simple Stack Overflow answer.)

The implementation of HTTP "basic" authentication consists of two components:

  1. On the server you want to go to the web site administration and add a userid and password to the folder that contains your web service (e.g. that PHP code referenced in that link you shared with us). The specifics vary a bit depending upon which web server you have. My ISP provides a "control panel" and in the "security" section there is a feature called "Directory Password". It may well be different for your web server, so talk to your ISP if it's not obvious.

  2. On the iOS side, rather than sendSynchronousRequest (which is a horrible solution, anyway; never do synchronous network requests), you want to employ the delegate based NSURLConnection and in addition to the standard didReceiveResponse, didReceiveData, connectionDidFinishLoading, and didFailWithError methods, you also want to write a didReceiveAuthenticationChallenge method that supplies the userid and password that you set up in the prior step.

On the iOS side, rather than sendSynchronousRequest, you want to do:

[NSURLConnection connectionWithRequest:request delegate:self];

and then implement the following delegate methods:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
        int statusCode = [(NSHTTPURLResponse *)response statusCode];
        if (statusCode != 200) {
            NSLog(@"%s failed status code %d", __FUNCTION__, statusCode);
        }
    }

    self.responseData = [NSMutableData data];
}

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

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // do whatever you want upon success

    NSLog(@"%s success; data = %@", __FUNCTION__, [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]);
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    // do whatever you want upon failure

    NSLog(@"%s: %@", __FUNCTION__, error);
}

-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]) {
        if ([challenge previousFailureCount] == 0) {
            NSURLCredential *credential = [NSURLCredential credentialWithUser:kUsername
                                                                     password:kPassword
                                                                  persistence:NSURLCredentialPersistenceForSession];

            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
        }
        else
        {
            [[challenge sender] cancelAuthenticationChallenge:challenge];

            // inform the user that the user name and password
            // in the preferences are incorrect

            NSLog (@"failed authentication");

            // ...error will be handled by connection didFailWithError
        }
    }
}

Obviously, this assumes you have defined a responseData property:

@property (nonatomic, strong) NSMutableData *responseData;

Now, I suspect that you're blanching at the notion of all of this code to send a request and authenticate that request. But doing proper HTTP-level authentication requires a solution like this. Convenience methods like sendSynchronousRequest (which you should avoid, regardless) and sendAsynchronousRequest (which is better, but still doesn't handle authentication challenges like this) just aren't up to the task. If you don't want to have to write all of this code, then you can consider using a library like AFNetworking that does all of this sort of code (plus much more), and gets you out of the weeds of handling network requests like this.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top