Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setDefaultCredential not working for UIWebView in iOS 7 but works fine in earlier iOS versions

I am currently using the following code to set default credentials for UIWebView. This works fine in iOS 6.1 and earlier. However, in iOS 7 Beta 6 it does not work at all.

The web pages that I am trying to load use Windows Authentication. I am able to open them in Safari in iOS 7. However, when I run the below code and then open the URL in a UIWebView, I get an empty white rectangle and nothing ever loads! Like I said, this works perfectly in iOS 6.1 and earlier.

I also tried a second approach that involves using NSURLConnectionDelegate to hand off the credentials. This second approach also works fine in iOS 6.1 and earlier, but is broken in iOS 7.

Does anyone know why this is happening? Similar experiences? Thoughts?

// Authenticate
NSURLCredential *credential = [NSURLCredential credentialWithUser:@"myusername"
                                                         password:@"mypassword"
                                                      persistence:NSURLCredentialPersistenceForSession];

NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
                                         initWithHost:@"mysite.com"
                                         port:80
                                         protocol:@"http"
                                         realm:nil
                                         authenticationMethod:NSURLAuthenticationMethodDefault];

[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace];
like image 251
jbro91837 Avatar asked Sep 08 '13 06:09

jbro91837


2 Answers

I had the same exact issue - an NSURLConnection to a Sharepoint site configured for Windows Authentication was working fine in iOS 6.1. In iOS 7 - regardless of whether I targeted and built the app for 6 or 7 - all authentications would seem to succeed (receiving the proper cookie) but still respond with a 401; all subsequent requests sent with the cookie would receive 401 as well.

I resolved the issue by dumping the didReceiveAuthenticationChallenge delegate protocol in favor of willSendRequestForAuthenticationChallenge. Implementing the 2nd delegate protocol means the first never gets called.

In your delegate, implement this delegate protocol:

- (void)connection:(NSURLConnection *)sender willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
   if ([challenge previousFailureCount] > 0]) {
       [[challenge sender] cancelAuthenticationChallenge:challenge];
   }else{
      NSURLCredential *credential = [NSURLCredential credentialWithUser:@"username" password:@"password" persistence:NSURLPersistenceForSession];
   [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
   }
}

After I implemented this on a whim, my iOS 7 NTLM authentication issues disappeared.

like image 169
ascary Avatar answered Nov 19 '22 16:11

ascary


Update: This issue appears to be fixed in iOS 7.0.3

I answered this in the Apple developer forum, but now that iOS7 is out of beta, I'll repost here. Currently Windows Authentication is broken in iOS7. I expect there will be a fix out shortly, but until then you can work around the problem by handling authentication challenges in your UIViewController that contains your UIWebView.

Essentially you

  1. Make an NSURLRequest and NSURLConnection yourself
  2. Handle the connection:didReceiveAuthenticationChallenge:
  3. In the connection:didReceivedResponse manually load your data into the UIWebView

Below I'm loading a PDF, but the process works the same whatever your content type.

//Make sure you implement NSURLConnectionDelegate and NSURLConnectionDataDelegate in your header

@interface MyViewController ()
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@property (strong, nonatomic) NSURLConnection *conn;
@property (strong, nonatomic) NSMutableData *pdfData;
@end

@implementation MyViewController 


//... all of your init and other standard UIViewController methods here...


//Method that loads UIWebview. You'll probably call this in viewDidLoad or somewhere similar... 
- (void) loadWebView {

    //Make Request manually with an NSURLConnection... 
    NSString *url = //Get your url
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    self.conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];

}

//#pragma mark - NSURLConnectionDelegate

//Handle authentication challenge (NSURLConnectionDelegate)
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    if([challenge previousFailureCount] == 0) {

        NSString *username = //Get username
        NSString *password = //Get password

        //Use credentials to authenticate
        NSURLCredential *cred = [NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistencePermanent];
        [[challenge sender] useCredential:cred forAuthenticationChallenge:challenge];

    } else {

        //Cancel authentication & connection
        [[challenge sender] cancelAuthenticationChallenge:challenge];
        [self.conn cancel];
        self.conn = nil;
    }
}

//#pragma mark - NSURLConnectionDataDelegate

//Received response (NSURLConnectionDataDelegate)
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {

    //Init data
    self.pdfData = [NSMutableData data];
}

//Collect data as it comes in (NSURLConnectionDataDelegate)
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.pdfData appendData:data];
}

//Handle connection failure (NSURLConnectionDataDelegate)
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {

    //Clean up... 
    [self.conn cancel];
    self.conn = nil;
    self.pdfData = nil;

    //TODO: Notify user and offer next step...
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

    //Finally, load data into UIWebview here (I'm loading a PDF)...
    [self.webView loadData:self.pdfData MIMEType:@"application/pdf" textEncodingName:@"utf-8" baseURL:nil];
}


@end
like image 45
jpolete Avatar answered Nov 19 '22 16:11

jpolete