Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to understand URLSession Authentication challenges

I am attempting to download a PDF from a URL.

private func downloadSessionWithFileURL(_ url: URL){
    var request = URLRequest(url: url)
    
    request.addValue("gzip, deflate", forHTTPHeaderField: "Accept-Encoding")
   
    let sessionConfig = URLSessionConfiguration.default
    
    let session = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
    session.downloadTask(with: request).resume()      
}

This calls its delegate method

func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
    if challenge.previousFailureCount > 0 {
          completionHandler(Foundation.URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
    }
    if let serverTrust = challenge.protectionSpace.serverTrust {
      completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: serverTrust))
} else {
      print("unknown state. error: \(String(describing: challenge.error))")
   }
}

The URLAuthenticationChallenges protectionSpace is always serverTrust. When the URL of the PDF is attempted to be accessed it redirects user to a login screen. I would have thought there would be another call to

func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)

requiring user to enter their credentials but there isn't. So the download task attempts to download the contents of the redirected URL which is a login screen.

My Questions are.

  1. What triggers a URLAuthenticationChallenge for a username and password. is it a specific header value in the HTML?

  2. Which URLAuthenticationChallenge protectionSpace should I be expecting for a username password request from a server.

like image 805
RyanTCB Avatar asked Oct 15 '25 14:10

RyanTCB


1 Answers

There are two different delegate protocols: for the URLSession itself, and its tasks.

URLSessionDelegate has: public func urlSession(_:didReceive:completionHandler:) URLSessionTaskDelegate has: public func urlSession(_:task:didReceive:completionHandler:)

The URLSessionDelegate is used for server trust issues (e.g. allowing SSL trust when running through Charles or other proxy). The URLSessionTaskDelegate is used for authentication of an individual task.

So to get your authentication challenge, add this to your class:

extension MyClass: URLSessionTaskDelegate {

    public func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodDefault ||
            challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic {

            let credential = URLCredential(user: self.basicAuthUserName,
                                           password: self.basicAuthPassword,
                                           persistence: .forSession)

            completionHandler(.useCredential, credential)
        }
        else {
            completionHandler(.performDefaultHandling, nil)
        }
    }
}
like image 191
Ben Avatar answered Oct 18 '25 09:10

Ben



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!