Reputation: 1809
In my iOS app, I would like to use a WKWebView to wrap an external URL in the application. This URL requires basic authentication (it needs user and password credential, like in the following screenshot).
After some investigation, I'm trying to use didReceiveAuthenticationChallenge
method, in order to enable an automatic login, so I'm not understanding how it works.
This is my code.
import UIKit
import WebKit
class WebViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView?
private var request : NSURLRequest {
let baseUrl = "https://unimol.esse3.cineca.it/auth/Logon.do"
let URL = NSURL(string: baseUrl)!
return NSURLRequest(URL: URL)
}
/* Start the network activity indicator when the web view is loading */
func webView(webView: WKWebView,
didStartProvisionalNavigation navigation: WKNavigation){
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
}
/* Stop the network activity indicator when the loading finishes */
func webView(webView: WKWebView,
didFinishNavigation navigation: WKNavigation){
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
func webView(webView: WKWebView,
decidePolicyForNavigationResponse navigationResponse: WKNavigationResponse,
decisionHandler: ((WKNavigationResponsePolicy) -> Void)){
decisionHandler(.Allow)
}
func webView(webView: WKWebView, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
if challenge.protectionSpace.host == "https://unimol.esse3.cineca.it/auth/Logon.do" {
let user = "*******"
let password = "******"
let credential = NSURLCredential(user: user, password: password, persistence: NSURLCredentialPersistence.ForSession)
challenge.sender?.useCredential(credential, forAuthenticationChallenge: challenge)
}
}
override func viewDidLoad() {
/* Create our preferences on how the web page should be loaded */
let preferences = WKPreferences()
preferences.javaScriptEnabled = false
/* Create a configuration for our preferences */
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
/* Now instantiate the web view */
webView = WKWebView(frame: view.bounds, configuration: configuration)
if let theWebView = webView {
/* Load a web page into our web view */
let urlRequest = self.request
theWebView.loadRequest(urlRequest)
theWebView.navigationDelegate = self
view.addSubview(theWebView)
}
}
}
I'm facing with this exception:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Completion handler passed to -[MyUnimol.WebViewController webView:didReceiveAuthenticationChallenge:completionHandler:] was not called'
If I delete the didReceiveAuthenticationChallenge
method, I'm able to reach the URL but is gives me, obliviously, wrong credentials.
Anyone could explain me what I'm doing wrong please?
Upvotes: 27
Views: 25869
Reputation: 4561
In a simpler way:
Though this answer looks redundant but posting as this may help other naive developers (like me).
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard (webView.url?.host) != nil else {
return
}
let authenticationMethod = challenge.protectionSpace.authenticationMethod
if authenticationMethod == NSURLAuthenticationMethodDefault || authenticationMethod == NSURLAuthenticationMethodHTTPBasic || authenticationMethod == NSURLAuthenticationMethodHTTPDigest {
let credential = URLCredential(user: userName, password: password, persistence: .none)
completionHandler(.useCredential, credential)
} else if authenticationMethod == NSURLAuthenticationMethodServerTrust {
completionHandler(.performDefaultHandling, nil)
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
Upvotes: 4
Reputation: 3393
i am late to party but still this can be useful for someone.
To support auth challenge in WKWebview Swift 4, complete code as below
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard let hostname = webView.url?.host else {
return
}
let authenticationMethod = challenge.protectionSpace.authenticationMethod
if authenticationMethod == NSURLAuthenticationMethodDefault || authenticationMethod == NSURLAuthenticationMethodHTTPBasic || authenticationMethod == NSURLAuthenticationMethodHTTPDigest {
let av = UIAlertController(title: webView.title, message: String(format: "AUTH_CHALLENGE_REQUIRE_PASSWORD".localized, hostname), preferredStyle: .alert)
av.addTextField(configurationHandler: { (textField) in
textField.placeholder = "USER_ID".localized
})
av.addTextField(configurationHandler: { (textField) in
textField.placeholder = "PASSWORD".localized
textField.isSecureTextEntry = true
})
av.addAction(UIAlertAction(title: "BUTTON_OK".localized, style: .default, handler: { (action) in
guard let userId = av.textFields?.first?.text else{
return
}
guard let password = av.textFields?.last?.text else {
return
}
let credential = URLCredential(user: userId, password: password, persistence: .none)
completionHandler(.useCredential,credential)
}))
av.addAction(UIAlertAction(title: "BUTTON_CANCEL".localized, style: .cancel, handler: { _ in
completionHandler(.cancelAuthenticationChallenge, nil);
}))
self.parentViewController?.present(av, animated: true, completion: nil)
}else if authenticationMethod == NSURLAuthenticationMethodServerTrust{
// needs this handling on iOS 9
completionHandler(.performDefaultHandling, nil);
}else{
completionHandler(.cancelAuthenticationChallenge, nil);
}
}
Upvotes: 16
Reputation: 1809
Add the following line:
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, credential)
at the end of didReceiveAuthenticationChallenge
solved the problem.
Upvotes: 25