Post

Replies

Boosts

Views

Activity

Reply to WKWebView Authorization Challenge fails to refresh
How we avoided or bypassed it using 2 load requests, as webview on subsequent reload or calls doesnt load the video and ends up with 401 status code and fails to call for refresh of challenge authentication We use 2 load, load via url session and then load web view. url sessions updates the username and password respectivly against web-load calls as well, thus loading video previews. This is not a fix but we want to portray that there's an issue. we would like to have your opinion, comment and suggestions. class ViewController: UIViewController { @IBOutlet weak var viewContainer: UIView! var currentWebView: WKWebView! private var authenticationSession: URLSession? let streams = [ "First Stream": ("url of the stream", "user", "password"), "Second Stream": ("url of the stream 2", "newuser", "secret") ] override func viewDidLoad() { super.viewDidLoad() setupWebView() } private func setupWebView() { let config = WKWebViewConfiguration() config.allowsInlineMediaPlayback = true config.mediaTypesRequiringUserActionForPlayback = [] let preferences = WKPreferences() preferences.javaScriptCanOpenWindowsAutomatically = false config.preferences = preferences config.websiteDataStore = WKWebsiteDataStore.default() currentWebView?.removeFromSuperview() currentWebView = WKWebView(frame: viewContainer.bounds, configuration: config) currentWebView.navigationDelegate = self currentWebView.scrollView.isScrollEnabled = false currentWebView.autoresizingMask = [.flexibleWidth, .flexibleHeight] currentWebView.isHidden = true viewContainer.addSubview(currentWebView) } private func setupURLSession(username: String, password: String) { let sessionConfig = URLSessionConfiguration.default sessionConfig.waitsForConnectivity = true sessionConfig.timeoutIntervalForRequest = 30 sessionConfig.timeoutIntervalForResource = 300 class AuthenticationDelegate: NSObject, URLSessionDelegate { let username: String let password: String init(username: String, password: String) { self.username = username self.password = password super.init() } func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { let credential = URLCredential(user: username, password: password, persistence: .forSession) completionHandler(.useCredential, credential) } } let delegate = AuthenticationDelegate(username: username, password: password) authenticationSession = URLSession(configuration: sessionConfig, delegate: delegate, delegateQueue: nil) } func loadStream(named streamName: String) async { guard let stream = streams[streamName] else { return } let (urlString, username, password) = stream setupWebView() currentWebView.isHidden = false await clearWebViewSession() self.setupURLSession(username: username, password: password) self.performStreamLoad( urlString: urlString, username: username, password: password ) } private func performStreamLoad(urlString: String, username: String, password: String) { guard let url = URL(string: urlString) else { print("Invalid URL") return } var request = URLRequest(url: url) request.cachePolicy = .reloadIgnoringLocalCacheData let loginString = "\(username):\(password)" if let loginData = loginString.data(using: .utf8) { let base64LoginString = loginData.base64EncodedString() request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization") } request.setValue("no-cache", forHTTPHeaderField: "Cache-Control") request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") let protectionSpace = URLProtectionSpace( host: url.host ?? "", port: url.port ?? 80, protocol: url.scheme, realm: nil, authenticationMethod: NSURLAuthenticationMethodHTTPBasic ) let credential = URLCredential( user: username, password: password, persistence: .forSession ) URLCredentialStorage.shared.setDefaultCredential(credential, for: protectionSpace) authenticationSession?.dataTask(with: request) { [weak self] _, response, error in DispatchQueue.main.async { if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 { self?.currentWebView.load(request) } else { print("Authentication failed with status code: \((response as? HTTPURLResponse)?.statusCode ?? 0)") self?.retryWithModifiedRequest(request: request, username: username, password: password) } } }.resume() } private func retryWithModifiedRequest(request: URLRequest, username: String, password: String) { var modifiedRequest = request modifiedRequest.setValue("Basic realm=\"Restricted Area\"", forHTTPHeaderField: "WWW-Authenticate") currentWebView.load(modifiedRequest) } } extension ViewController: WKNavigationDelegate { func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard let url = webView.url else { completionHandler(.cancelAuthenticationChallenge, nil) return } if let stream = streams.first(where: { $0.value.0 == url.absoluteString.split(separator: "?").first.map(String.init) ?? "" }) { let (_, username, password) = stream.value let credential = URLCredential(user: username, password: password, persistence: .permanent) completionHandler(.useCredential, credential) } else { completionHandler(.performDefaultHandling, nil) } } func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { decisionHandler(.allow) } }
Nov ’24