When using a Background Session Configuration instead of the default to call a HTTPS URL the serverTrustPolicyManager is never used and the download fails with "An SSL error has occured and a secure connection to the server cannot be made.".
I create my Manager like this
let serverTrustPolicies: [String: ServerTrustPolicy] = [
Consts.host: .PinCertificates(
certificates: ServerTrustPolicy.certificatesInBundle(),
validateCertificateChain: true,
validateHost: false
)
]
let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("projectid.background")
let downloadManager = Alamofire.Manager(
configuration: configuration,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)
The didReceiveChallenge of the SessionDelegate is never called. Using NSURLSessionConfiguration.defaultSessionConfiguration() everything works as expected and the SSL certificate is successfully validated. Does anyone have an idea why the didReceiveChallenge is not called when using a background configuration? I couldn´t figure it out yet.
I am using XCode 7.2.1 testing on iOS 9.2.1.
This is a great question @matthiaslau. Personally I haven't tried to use cert pinning yet with a background session. If anyone else has any info here, please share!
Are you running into this problem when the app is running in the background, or when it's running in the foreground? There are some obvious problems right now with the Alamofire design working with background sessions. Such as trying to reconnect your session and all the response closures being dropped.
Could you give me a more detailed explanation of your use case?
This problem directly occurs regardless of whether in foreground or background. In my usecase we are communicating with a server running on a hardware we are producing using certificate pinning for security reasons. When we are downloading files from this server I want the app to continue in the background so the download is finished even when the user leaves the app.
The interesting part is that without putting the app into background didReceiveChallengeis not called just because the session configuration is different.
@cnoon: do you have a rough idea where I can look at for further investigation? Thanks for your help! :-D
Hey @matthiaslau, have you made any progress here?
Have you been able to successfully use cert pinning with your server without using background sessions? That is the first thing you should probably try to get working. To do this, you'll want to switch to a defaultURLSession and try to get everything working. I'm fairly confident that you're probably running into an ATS issue or maybe you have your server trust set up incorrectly. Starting here will guarantee you have ATS and cert pinning configured correctly. 👍🏼
Once you get everything working on a default URL session, then I'd suggest moving to a backgroundURLSession and troubleshoot. I'm fairly confident that this will just work for you after you have everything working for a default session. I think your issue is with ATS or your server trust and not actually with background session.
I'm going to close this issue out for now. If you end up finding that there is actually an issue in Alamofire for this case, please provide some more info and I'll happily re-open and help you investigate further.
Best of luck! 🍻
Hi @cnoon ,
thanks for your answer :beer:. Our App is successfully running with the defaultConfiguration in production, so no problems with the server or certificate pinning in general. As I wrote, while troubleshooting I recognized that didReceiveChallenge is never called after I switched from the defaultConfiguration to the backgroundSession (no other changes). I couldn´t find out yet what´s the reason for that. I am thankful for any idea where to continue my troubleshooting.
Cheers
Hi @mattcantstop,
Sounds like this could use some additional investigation on our end. From what I'm hearing though, it sounds like background sessions are not respecting auth challenges in the same way as a default session. That's potentially problematic since Alamofire doesn't control any of that logic. It just reacts to it.
Would you be willing to put together a test case that can produce this behavior? It would much easier for us to help you debug if you could. Then we can try to dig in around the passing case and failing case and see if we can figure out what's going on.
Thanks!
Sure, I hope to find some time this weekend for further troubleshooting and writing a testcase for this.
@matthiaslau are you still planning on putting together a sample app for this issue? If not, I'm going to go ahead and close this out.
Hi @cnoon and @matthiaslau,
I wrote a test, checking whether didReceiveChallenge will be called, when using a background configuration. It fails even w/o Certification Pinning. (Alamofire 3.4.0, Xcode 7.3.1, iOS 9.3)
@cnoon wrote a test where it does succeed with default config.
It seems, session:didReceiveChallenge won't be called with a background configuration at all, even tough the SessionDelegate and the implementations do exist. Also session:task:didReceiveChallenge won't be called. (in the default Alamofire implementation of SessionDelegate, it would call session:didReceiveChallenge)
I couldn't find any hints in Apple's documentation, mentioning restrictions regarding authentication and background sessions.
Background Transfer Considerations
possible next steps
Next thing I would like to try is whether the delegate's function still won't be called when setting up a NSURLSession w/ background config but w/o Alamofire.
Also, I would like to try whether it would help, adding the certificate to the shared NSURLCredentialStorage (which is used by default and background sessions) or to a custom Request object before starting a request (as a possible fix). Because iOS will look there before calling didReceiveChallenge.
Next, if your delegate agrees to handle authentication and there are no valid credentials available, either as part of the requested URL or in the shared NSURLCredentialStorage, the delegate receives one of the following messages:
URLSession:didReceiveChallenge:completionHandler:
URLSession:task:didReceiveChallenge:completionHandler:
Or whether using a download task rather than a data task would make a difference. Data tasks were not supported in background sessions prior to iOS 8 and may still be more restricted.
Hi @matthiaslau and @bclausdorff,
Sorry for not digging into this sooner. I finally found some time to really dig into this issue. I've scoured all the Apple docs and I can't find any definitive answer on the subject as to whether the background session configuration automatically evaluates a server trust challenge for you. Because of this, I decided to build a sample app to test it out.
What I found in the sample app is that with background sessions, server trust challenges are automatically evaluated. You'll never get called when performing the TLS handshake. iOS is just going to evaluate the cert and establish the connection if the cert chain is valid and fail if it's not. I tested both cases with https://httpbin.org/get and https://expired.badssl.com/. The challenge delegate API just isn't called in either case running on a device or in the simulator. I am assuming this is for performance reasons. If you end up needing to do something like cert pinning or allowing a connection if the cert is invalid (self-signed certs), then you just can't use a background session.
What was interesting is that the delegate is called on a basic-auth challenge. Now from what I can gather from the Handling Authentication docs is that basic-auth challenges are non-session-level challenges. I can only assume that these types of challenges are not automatically evaluated on background sessions, where session-level challenges are.
Long story short, there's nothing we can do here. Server trust challenges are automatically handled by iOS with background session configurations. As such, I'm going to go ahead and close this issue out since there's nothing we can actually do about it in Alamofire.
Thanks everyone for being patient here until we had time to get to the bottom of the behavior.
Cheers. 🍻
Hi @cnoon,
thank you for investigating this!
That seems to be quite an edge case we got ourselves into here...
Cheers!
Most helpful comment
Hi @matthiaslau and @bclausdorff,
Sorry for not digging into this sooner. I finally found some time to really dig into this issue. I've scoured all the Apple docs and I can't find any definitive answer on the subject as to whether the background session configuration automatically evaluates a server trust challenge for you. Because of this, I decided to build a sample app to test it out.
What I found in the sample app is that with background sessions, server trust challenges are automatically evaluated. You'll never get called when performing the TLS handshake. iOS is just going to evaluate the cert and establish the connection if the cert chain is valid and fail if it's not. I tested both cases with
https://httpbin.org/getandhttps://expired.badssl.com/. The challenge delegate API just isn't called in either case running on a device or in the simulator. I am assuming this is for performance reasons. If you end up needing to do something like cert pinning or allowing a connection if the cert is invalid (self-signed certs), then you just can't use a background session.What was interesting is that the delegate is called on a basic-auth challenge. Now from what I can gather from the Handling Authentication docs is that
basic-authchallenges are non-session-level challenges. I can only assume that these types of challenges are not automatically evaluated on background sessions, where session-level challenges are.Long story short, there's nothing we can do here. Server trust challenges are automatically handled by iOS with background session configurations. As such, I'm going to go ahead and close this issue out since there's nothing we can actually do about it in Alamofire.
Thanks everyone for being patient here until we had time to get to the bottom of the behavior.
Cheers. 🍻