iOS & WebAPIs & HTTPS Debugging
I recently have been creating a lot of ASP.NET Web APIs with VS Code to serve as backends for my mobile apps.
(I am enamored by the ability to deploy ASP.NET Web APIs into a Docker Containers. And then run those containers in Azure's Linux App Service. Reasons why in a future post!)
Of course I want to debug both the Web API and the mobile app at the same time during development.
This should be an easy task. Unfortunately on iOS, there are some hoops to jump through.
In this post I'm going to show you how to do it. Both the steps I tried, and the reasons why. Read through it and you won't have to suffer like I did to debug ASP.NET Web APIs from Xamarin.iOS apps.
(Check out how to do it on Android here.)
Starting Point
To recreate the situation, open up VS Code in a blank folder and enter the following in the terminal:
dotnet new webapi -n NoSSL.API
That will create a basic template for an ASP.NET Web API. With a project name of NoSSL.API
.
From the same terminal window, type:
dotnet run
That will start everything up. And notice it'll be running on http://localhost:5000
and https://localhost:5001
Go into your favorite web browser and type: https://localhost:5001/api/values
.
Continue on if it warms about security. After it loads, you'll see a JSON string returned.
Invoking From a Xamarin App
The code used to invoke the Web API will be the following:
An HttpClient
that uses the GetStringAsync
function. Nothing too fancy.
The First Attempt
First, I tried running the code above with everything as is. I got an error with the message:
The certificate for this server is invalid. You might be connecting to a server that is pretending to be “localhost” which could put your confidential information at risk.
OK. Sounds like iOS doesn't trust the certificate that Kestrel is using. The dotnet
tooling comes with an extension that installs certificates. So let's try that.
Run the following in the terminal:
dotnet dev-certs https -t
This will install a development certificate. And browsers will stop complaining about untrusted certificates.
But run the iOS app again and nothing changes, the same error appears.
Hmm...
The Second Attempt
Knowing that I could not get iOS to trust the certificate, I decided to use the non-SSL version of the service.
The service starts at both http://localhost:5000
and https://localhost:5001
. So why not try the non-SSL version?
I added the following key to the Info.plist
file.
The NSAllowsLocalNetworking
key tells iOS to not enforce its SSL rules on localhost.
Run it again. Get the same error.
Why?!?
The Third Attempt
At this point I noticed the output window in VS Code was showing the request was trying to connect through SSL yet.
Weird.
Only one thing left to do now. Change the launchSettings.json
file to only launch on HTTP.
Upon opening it, I found a line that said:
"applicationUrl":"https://localhost:5001;http://localhost:5000"
I got rid of the https://localhost:5001
part of it. Started up the Web API again. Then tried the mobile app again.
Success!
The Moral of the Story
I got stuck between attempt 2 and attempt 3 for quite a while. It didn't make sense that the Kestrel web server would try to route that non-SSL request to SSL. (Still doesn't make sense, let me know if you know why.)
But the moral here is to enable NSAllowsLocalNetworking
and make sure you're only running a non-SSL version of the Web API. Then you'll be good to go.
Oh, and should you ship and app with NSAllowsLocalNetworking
allowed? No. Use PListBuddy
in a custom build script to swap it out during your DevOps production build pipeline. See the Azure DevOps or Visual Studio App Center docs for more info.