Thursday, 17 October 2019

Xamarin: Connecting to localhost over HTTPS from simulators and emulators

Most mobile apps consume web services. During the development phase, it’s common to deploy a web service locally and consume it from a mobile app running in a simulator or emulator.

Consuming localhost web services that run over HTTP is straight forward enough. However, it’s more work when the web service runs over HTTPS. This involves:
  • Creating a self-signed developer certificate on your machine.
  • Choosing the network stack to use.
  • Specifying the address of your local machine.
  • Bypassing the local development certificate check.
This process is documented at Connect to Local Web Services from iOS Simulators and Android Emulators.

On iOS and Android, attempting to invoke a local secure web service from an app running in the iOS simulator or Android emulator results in an exception, even when the managed network stack is used. This is because the local HTTPS development certificate is self-signed, and self-signed certificates aren’t trusted by iOS or Android.

There are a number of approaches that can be used to work around this issue, but the typical approach was to use the managed HttpClient implementation in your DEBUG builds, and then set a callback for the System.Net.ServicePointManager.ServerCerticateValidationCallback that ignores the result of the localhost certificate check. This approach worked for both iOS and Android. However, in the last month it stopped working on Android and has left people wondering why.

In the last month, the new CoreFX implementation of HttpClient was dropped into the following Mono profiles:
  • Desktop Mono on Linux and OS X.
  • Web Assembly
  • Android
This implementation does not include the ServicePointManager API, because it’s not part of .NET Core. Instead, it includes the HttpClientHandler.ServerCertificateCustomValidationCallback property (API doc). Therefore, currently, the process for ignoring SSL certificate errors on Android has diverged from iOS.

SSL errors can be ignored on Android for local secure web services by setting the ServerCertificateCustomValidationCallback property to a callback that ignores the result of the certificate security check for the localhost certificate:
        public HttpClientHandler GetInsecureHandler()
        {
            var handler = new HttpClientHandler();
            handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
            {
                if (cert.Issuer.Equals("CN=localhost"))
                    return true;
                return errors == System.Net.Security.SslPolicyErrors.None;
            };
            return handler;
        }
This HttpClientHandler object returned by the GetInsecureHandler method should be passed as an argument to the HttpClient constructor. The advantage of using this new approach is that it hooks into the AndroidClientHandler native network stack, which is the recommended network stack on Android. Therefore, it’s no longer necessary to use the managed network stack on Android during development.

On iOS, it’s still recommended to use the managed network stack during development, with the old ServicePointManager API. However, iOS support for the CoreFX HttpClient implementation is in the works, and will hook into the NSUrlSession network stack. Once it’s released, the same approach to bypassing localhost certificate checks can be used on iOS and Android.

For a full sample that demonstrates this approach in Xamarin.Forms, see my GitHub repo.