Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authentication Challenge Method Is Not Called When Using NSURLSession Custom Delegate

I'm working on an iOS app which connects to an ASP.NET Web API through Restful services. I want to use a custom delegate to handle authentication challenge. But the delegate method doesn't get called.

The http request is written in the following method within a view controller:

- (IBAction)test:(UIButton *)sender
{
    //Get Bearer Token
    KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc]      initWithIdentifier:@"BearerToken" accessGroup:nil];
    NSString *bearerToken = [keychainItem objectForKey:(__bridge id)(kSecValueData)];

    //Configure request
    NSURL *url = [NSURL URLWithString:@"......"]; //Replace the .... with real IP Address
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"GET"];
    [request setValue:[NSString stringWithFormat:@"Bearer %@", bearerToken] forHTTPHeaderField:@"Authorization"];

    //Configure session
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];

    AuthChallengeDelegate *authChallengeDel = [[AuthChallengeDelegate alloc] init];

    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
                                                          delegate:authChallengeDel
                                                     delegateQueue:nil];

    NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
    [task resume];
}

In the AuthChallengeDelegate class, I have implemented the following method:

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
    NSLog(@"%@", response);   
}

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
                             NSURLCredential *credential))completionHandler
{
    NSLog(@"did receive challenge method called");
    NSLog(@"%@", challenge.protectionSpace.authenticationMethod);
}

The first method (didReceiveResponse) get called and the response status code is 401 with "Www-Authenticate" = Bearer in the header field. But the second method (didReceiveChallenge) is not called. Anyone here could give me an idea of why it's not called?

(I'm using Xcode 6 and simulating in iOS8)

Thanks.

like image 488
Dean Avatar asked Oct 09 '14 00:10

Dean


2 Answers

There are two different challenge/response handlers in NSURLSession's delegates. The first, which you're implementing, is at the session level, and basically handles server-level authentication. From the documentation:

  • For session-level challenges—NSURLAuthenticationMethodNTLM, NSURLAuthenticationMethodNegotiate, NSURLAuthenticationMethodClientCertificate, or NSURLAuthenticationMethodServerTrust—the NSURLSession object calls the session delegate’s URLSession:didReceiveChallenge:completionHandler: method. If your app does not provide a session delegate method, the NSURLSession object calls the task delegate’s URLSession:task:didReceiveChallenge:completionHandler: method to handle the challenge.

  • For non-session-level challenges (all others), the NSURLSession object calls the session delegate’s URLSession:task:didReceiveChallenge:completionHandler: method to handle the challenge. If your app provides a session delegate and you need to handle authentication, then you must either handle the authentication at the task level or provide a task-level handler that calls the per-session handler explicitly. The session delegate’s URLSession:didReceiveChallenge:completionHandler: method is not called for non-session-level challenges.

So, you probably want to handle task-level authentication by adding protocol support for NSURLSessionTaskDelegate in your delegate object, and supplying a handler at the task level, i.e. URLSession(_:task:didReceiveChallenge:completionHandler:).

like image 164
Matt Gibson Avatar answered Nov 14 '22 21:11

Matt Gibson


From iOS developer library

Important: The URL loading system classes do not call their delegates to handle request challenges unless the server response contains a WWW-Authenticate header.

From where we understand that didReceiveChallenge methods should be called BUT the method URLSession:task:didReceiveChallenge:completionHandler: gets called only when the header looks like 'WWW-Authenticate':'Basic'

I haven't found any solution how to handle token based authentication where header is 'WWW-Authenticate':'Bearer' as in question using Authentication Challenges.

like image 21
Artur Avatar answered Nov 14 '22 20:11

Artur