0

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.

Community
  • 1
  • 1
benpva16
  • 446
  • 6
  • 26

1 Answers1

1

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.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • >> Now, I suspect that you're blanching at the notion of all this code to send a request and authenticate that request. Well, sort of not really. I've implemented this because I was originally trying to do an SFTP upload (the whole point of this thing is to upload a file), but I couldn't find how to implement such a library. Do you have any helpful resources? Tutorials would be extremely useful. – benpva16 Mar 11 '14 at 16:09
  • @benjsigmon As you might infer from that other answer you quoted in your question, people generally use HTTP to upload files (frankly, I assumed that in your referencing that answer, that you were saying that you were going to use HTTPS rather than SFTP). That's far more common than SFTP (and easier, IMHO). If you absolutely have to use SFTP, perhaps this answer will help: http://stackoverflow.com/questions/14462257/sftp-libraries-for-iphone. Or google "iOS sftp library". – Rob Mar 11 '14 at 16:23
  • Actually, I didn't know that. In all my app development, I've never worked with sensitive enough data to need anything other than email. Anyway, thanks for the help. This tiny feature I need in my app is consuming the majority of my time. Pareto principle and all that. – benpva16 Mar 11 '14 at 16:36