I'm running into a problem where my
-URLSession:task:didReceiveChallenge:completionHandler:delegate method is not getting called for every request I make.
This is expected behaviour, and it speaks to a fundamental disconnect between HTTP and TLS authentication. HTTP authentication, like Basic and Digest, is per request; the authentication information is carried in headers in the request and response. TLS authentication is per connection; authentication is done when you open the connection. The disconnect is that multiple HTTP requests can run over the same connection. This is common in HTTP 1.1 (with HTTP 1.1 persistent connections) and almost guaranteed by HTTP 2 (where there’s a single connection over which all requests and responses travel).
NSURLSession exposes this difference via its delegate callbacks:
Per connection authentication challenges are delivered via
Per request authentication challenges are delivered via
Now, if you don’t implement both handlers then, as a convenience, NSURLSession can deliver authentication challenges to the ‘wrong’ handler. The table below shows all the combinations.
Delegate Callbacks Request Challenge Connection Challenge both task session task task task session - session
However, in my experience it’s a bad idea to rely on this because you’re ignoring a key element of how challenges are structured.
how can I make sure that the delegate method is called for every request OR how can get the certificate chain from an NSURLSessionDataTask if that delegate method isn't called?
You can’t. Once a session has established a connection to a host, any request to that host may be sent over that connection. In fact, it’s possible for a request’s transactions to be split over multiple connections. For example, if you make a request that’s redirected, the initial transaction might go over connection A while the redirected transaction might go over connection B.
The way to avoid problems with this is to make sure that all of the connections to a given host within a given session are equivalent from a security perspective. You can then authenticate the session’s connections and know that all subsequent requests in that session are running over an authenticated connection.
In my experience this design is fine in most circumstances. However, there are certain situations where you need multiple connections to a server that are not equivalent security-wise. For example, you might want to make sure that some requests go over a connection that’s been authenticated with a client identity while other requests go over a connection without that that client identity. Fortunately it’s possible to do that: create two sessions, one for each type of connection.
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"