Saturday, September 21, 2019

URLSession Custom Server Trust Evaluation

In certain situations where the server certificate is not configured properly, or when using self signed SSL certificates, requests validation fails with URLSession. URLSession provides delegate methods which can be used in situations like this and can be used as a workaround until the certificate gets fixed on the server end. We can also use manual certification validation if we want to pin to a particular certificate. To do this, we need to assign a delegate for the URLSession object and handle the validation there as shown in the snippet below.
class NetworkService: NSObject {
private lazy var opsQueue: OperationQueue = {
let q = OperationQueue.init()
q.name = "api queue"
q.qualityOfService = QualityOfService.background
return q
}()
private lazy var session: URLSession = {
return URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: opsQueue)
}()
}

extension NetworkService: URLSessionDelegate {
/// Customise server trust evaluation in case where server certificate validation fails.
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let protectionSpace: URLProtectionSpace = challenge.protectionSpace
if protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
if let trust: SecTrust = protectionSpace.serverTrust {
let cred = URLCredential(trust: trust)
completionHandler(URLSession.AuthChallengeDisposition.useCredential, cred) // Say, custom SSL, so allow
} else {
completionHandler(URLSession.AuthChallengeDisposition.performDefaultHandling, nil)
}
} else {
completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
}
}
}
Here the URLCredential object containing the SecTrust is passed to the completionHandler which makes the request go through. We can also do additional validation on the URLProtectionSpace and so on.