WKWebView authentication challenge crashes in iOS 10.3 beta 2

Hi!


I have an WKWebView that opens an web page that requires basic authentication. This is how it is handled:


    func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        if challenge.protectionSpace.host == "correcturl.com" {
            let user = "user"
            let password = "password"
            let credential = URLCredential(user: user, password: password, persistence: URLCredential.Persistence.forSession)
            challenge.sender?.use(credential, for: challenge)
            completionHandler(.useCredential, credential)
        }

        completionHandler(.performDefaultHandling, nil)
    }


However on iOS 10.3 beta 1 and 2, ths code crashes at line 06 with the following error:


2017-02-13 14:33:31.094358+0200 MyApp[262:8266] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'The challenge was not sent by the receiver.'
*** First throw call stack:
(0x18c966fb8 0x18b3cc538 0x18c966f00 0x196587f2c 0x196587f94 0x1002db838 0x1002dba80 0x19637e5e4 0x1964c40e0 0x1963c7abc 0x1963c9c9c 0x196340ec8 0x1963436b4 0x19128c70c 0x19128c9e4 0x18c915404 0x18c914d74 0x18c912980 0x18c842d74 0x18e2ab074 0x192b9f988 0x10028b600 0x18b85159c)
libc++abi.dylib: terminating with uncaught exception of type NSException


I searched around a bit, and found some code in a WebKit repository that gave me some hint about the source of an error:


static void checkChallenge(NSURLAuthenticationChallenge *challenge)
{
  if ([challenge class] != [WKNSURLAuthenticationChallenge class])
  [NSException raise:NSInvalidArgumentException format:@"The challenge was not sent by the receiver."];
}


Based on this, I did some type checking on the variables, and appears that the type of the "challenge" variable is different in iOS 10.2 and 10.3


in iOS 10.3 beta 1 and 2 it is WKNSURLAuthenticationChallenge

in iOS 10.2 it is NSURLAuthenticationChallenge

Initially this seems to be a bug somewhere in iOS (or WebKit shipped with it), but not sure. Thoughts?

Hi satellink,


I'm also facing the same issue.

Do you find any solution for the same.


Thanks in Advanced.

Pravin Agrawal

It works by removing (or comment out) this line.


            challenge.sender?.use(credential, for: challenge)


I don't know why, but it becomes working.

Thanks, @peecu,

Yes, it is also working fine for me in Xcode 8.3.

I have also verified it in other iOS versions 9.X, 10.1, 10.2 and 10.3.

It seems like in Xcode 8.3 and Apple Swift version 3.1 (swiftlang-802.0.48 clang-802.0.38)

Target: x86_64-apple-macosx10.9 the 'completionHandler(.useCredential, credential)' by default manage the 'NSURLAuthenticationChallenge *challenge' with the help of URLSession.AuthChallengeDisposition.useCredential.

Having exactly the same issue

func webView(webView: WKWebView, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
        taskingLogObject.logMessage("AuthenticationMethod: \(challenge.protectionSpace.authenticationMethod)")
      
        switch (challenge.protectionSpace.authenticationMethod) {
        case NSURLAuthenticationMethodServerTrust:
    
            taskingLogObject.logHostInfo(challenge.protectionSpace.host)
          
            let credential =  NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!);
            completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, credential)
          
            break
        case NSURLAuthenticationMethodNTLM, NSURLAuthenticationMethodNegotiate:
          
            if self.userAuthForm == true {
              
                /
                /
                if challenge.protectionSpace.host == "some host" {
                    /
                    /
                    preferences.javaScriptEnabled = true
                  
                    displayAlertAuth({ (challenge, credential) in
                        completionHandler(challenge, credential)
                    })
                }
              
            } else {
              
                let dictionary = Locksmith.loadDataForUserAccount(Constants.KeyChainKeys.techKey.rawValue)
                guard let password = dictionary?["password"] as? String where password.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) > 0 else {
                    /
                    completionHandler(NSURLSessionAuthChallengeDisposition.PerformDefaultHandling, nil)
                    return
                }
              
                guard let username = ProfileModel.sharedInstance.ntId else {
                    /
                    completionHandler(NSURLSessionAuthChallengeDisposition.PerformDefaultHandling, nil)
                    return
                }
              
                let credentials = NSURLCredential(user: username, password: password, persistence: NSURLCredentialPersistence.Permanent)
               //crashing line - really APPLE?!!
                 challenge.sender?.useCredential(credentials, forAuthenticationChallenge: challenge)   //(credentials, for: challenge)

              
                if currentNumberOfChallangeRetries >= maxNumberOfChallangeRetries{
                    webView.stopLoading()
                    writelog("Tasking: Reached max number of challanges")

                        let alertView = ETAlertMessages(title: "Attention!", message: "Tasking is unavailable, please try again later.")
                        alertView.addAlertAction(Constants.AlertNotifications.OkButton.rawValue) { () -> () in
                        }
                        alertView.show(self, animated: true) { () -> () in
                        }
                  
                    completionHandler(NSURLSessionAuthChallengeDisposition.PerformDefaultHandling, nil)
                }
                else{
                    currentNumberOfChallangeRetries += 1
                    completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, credentials)
                }
           
              
            }
          
            break
        default:
            /
            /
            completionHandler(NSURLSessionAuthChallengeDisposition.PerformDefaultHandling, nil);
            break
        }
      
    }

Thanks for the suggestion, however when attempting this in a legacy Obj-C app running on iOS10.3 b2, the delegate method seems to be called in an infinite loop, since it doesn't seem to actually get confirmed/rejected by the completion handler.


This bug is quite a game breaker for us at the moment. Any additional suggestions/solutions are definitely appreciated!

Oh, thank you for the solution!

I did submit a bug report to Apple a while ago; I now updated the (already closed) bug with more info & link to this thread.

WKWebView authentication challenge crashes in iOS 10.3 beta 2
 
 
Q