DiUS/pact-consumer-swift

ATS blocking mockserver interaction

thomas-br opened this issue · 4 comments

I was setting up pact-consumer-swift following your guide but struggling currently during the execution of my contract test.

By default my test is failing due to an ATS error:

Error Domain=NSURLErrorDomain Code=-1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." UserInfo={NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection., NSErrorFailingURLStringKey=http://localhost:1234/interactions/verification, NSErrorFailingURLKey=http://localhost:1234/interactions/verification
[...]
Verification error (check build log for mismatches): The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.

Also setting the following settings to my TestTarget's Info.plist does not work:

<key>NSExceptionDomains</key>
	<dict>
		<key>localhost</key>
		<dict>
			<key>NSExceptionAllowsInsecureHTTPLoads</key>
			<true/>
		</dict>
	</dict>

It does work though when setting it to the actual App-Target's plist, where I am then able to successfully run my contract test.
But setting it on the App generally is not what I want, also when looking at it from a security point of view or regarding a potential AppStore review.

How are you guys dealing with it normally?
I haven't found anything related to ATS in the docs here or in any example project.

You can start the pact-ruby-standalone with self signed ssl and run your tests on https://localhost. And in your api code you can set it to accept self signed certs on localhost only. That way you don't need to edit the ATS of your app.

Check the unit tests in this repo for an example. (I'm writing this on a mobile phone, but if you still struggle I can later point you to the code that handles ssl situations)

An example on how to start the pact-ruby-standalone with SSL (see scripts/start_server.sh):
pact-mock-service start --ssl --pact-specification-version 2.0.0 --log "${SRCROOT}/tmp/pact-ssl.log" --pact-dir "${SRCROOT}/tmp/pacts-ssl" -p 2345

You need to have your API client implementation's URLSession conform to URLSessionDelegate and have the challenge accept the unsecure localhost in delegate method urlSession(_:didReceive:completionHandler). Example here. This allows you to not have to change the ATS config, setting exceptions in plists etc. Your iOS client is expected to always communicate over SSL, which is good, and you are not making any exceptions except for insecure (self-signed) SSL certs on localhost.

And when writing your pact tests you set up your mockService with a verification service that's running on https as in this example test.

The reason your tests succeed when changing the setting on the actual App's target is because you are (should be) testing your App's API client implementation. And you're testing that it does what you expect it to and what it needs to do, eg: make a network request over http(s) to a provider (server) and that it can handle the "expected" response.

Okay, thank you for the clarification.

@thomas-br if you are still experiencing trouble getting https://localhost running, we've encountered an anomaly as described in this comment.