Thursday, 30 April 2020

Connecting to locahost over HTTPS from simulators and emulators (revisited)

Last year, I wrote about how to connect to localhost over HTTPS from iOS simulators and Android emulators. Connecting to localhost web services that run over HTTP is straight forward, however it’s more work when the web service runs over HTTPS. It typically 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.

Connect to local web services from iOS simulators and Android emulators documents how to carry out the above steps. However, I recently discovered that the approach for bypassing the local development certificate check on iOS no longer worked.

At the time of writing the original blog post, the CoreFX implementation of HttpClient was in most Mono profiles, but not in iOS. The recommendation for iOS was to use the managed network stack, and the ServicePointManager API. However. since then iOS support for the CoreFX HttpClient implementation has dropped, which hooks into the NSUrlSession network stack. This ultimately means that now, on both iOS and Android, the approach for bypassing the local development certificate check is identical.

SSL errors can be ignored on iOS and Android for local HTTPS web services by setting the HttpClientHandler.ServerCertificateCustomValidationCallback property to a callback that ignores the result of the certificate security check for the localhost certificate:

public HttpClientHandler GetInsecureHandler()
{
    HttpClientHandler 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 method must be in a class in a platform project, even if the HttpClient object is constructed in a shared project. The HttpClientHandler object returned by the GetInsecureHandler method should then be passed as an argument to the HttpClient constructor:

HttpClient client = new HttpClient(GetInsecureHandler());
The advantage of using this approach is that it hooks into the native network stacks on both iOS and Android (NSUrlSession on iOS, AndroidClientHandler on Android).

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

Wednesday, 8 April 2020

Combining Xamarin.Essentials WebAuthenticator with IdentityModel.OidcClient

Previously I wrote about using the Xamarin.Essentials WebAuthenticator class to initiate an authentication flow with IdentityServer, and wait for a callback/redirect to the redirect URI scheme. Although my blog post didn’t show it, there was an IdentityService class that created the URI for the authorization endpoint, including the code challenge, CSRF token, and other required query parameters. This class also handled exchanging the received authorization code for an access token, which could then be used in API calls.

While this approach works well, it does require a detailed knowledge of the OAuth 2.0 spec for native apps. While that’s certainly something I’d recommend understanding fully, it would also be convenient if there was a library that did the heavy lifting for you.

Enter IdentityModel.OidcClient

IdentityModel.OidcClient is a C#/.NET standard 2.0 OpenID certified library for native mobile and desktop applications. It’s a small, simple, and elegant library that I strongly recommend for communicating with IdentityServer.

The library has two modes:

  1. In manual mode, OidcClient helps you with creating the necessary start URL and state parameters, but you need to coordinate with the browser you want to use. Then, when the browser work is completed, OidcClient can take over to process the response, get the access/refresh tokens, and contact other endpoints.
  2. In automatic mode, you can encapsulate all browser interactions by implementing the IBrowser interface. Then, authentication and token requests become one line of code.

I’ve previously used manual mode for interacting with IdentityServer, when I had to write my own platform code for invoking native browsers. However, now that WebAuthenticator does that for you, it’s much simpler to use automatic mode and wrap WebAuthenticator using the IBrowser interface.

The IBrowser interface is used to model a browser, and specifies that a single method named InvokeAsync requires implementing, which returns a Task<BrowserResult>. The InvokeAsync method should just invoke your browser to initiate the authentication flow, and return the response in a BrowserResult object.

A Browser class that implements the IBrowser interface, which uses WebAuthenticator to provide the browser interactions, is shown below:

public class Browser : IBrowser
{
    public async Task InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
    {
        WebAuthenticatorResult authResult =
                await WebAuthenticator.AuthenticateAsync(new Uri(options.StartUrl), new Uri(Constants.RedirectUri));
        return new BrowserResult()
        {
            Response = ParseAuthenticatorResult(authResult)
        };
    }

    string ParseAuthenticatorResult(WebAuthenticatorResult result)
    {
        string code = result?.Properties["code"];
        string scope = result?.Properties["scope"];
        string state = result?.Properties["state"];
        string sessionState = result?.Properties["session_state"];
        return $"{Constants.RedirectUri}#code={code}&scope={scope}&state={state}&session_state={sessionState}";
    }
}

Here, the InvokeAsync method calls the WebAuthenticator.AuthenticateAsync method, and returns the parsed response in a BrowserResult object.

An authentication flow can then be initiated by a OidcClient object as follows:

OidcClient _oidcClient;
LoginResult _loginResult;

var options = new OidcClientOptions
{
    Authority = Constants.AuthorityUri,
    ClientId = Constants.ClientId,
    Scope = Constants.Scope,
    RedirectUri = Constants.RedirectUri,
    ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect,
    Browser = new Browser()
};

_oidcClient = new OidcClient(options);
_loginResult = await _oidcClient.LoginAsync(new LoginRequest());

Here, an OidcClientOptions object is created that defines the precise authentication flow that will be invoked with IdentityServer. The OidcClientOptions object also defines the IBrowser object that's used to provide browser interactions. An OidcClient object is then created, and its LoginAsync method is invoked to initiate the authentication flow. The result of the authentication flow will then be stored in a LoginResult object, which can include different tokens (access, identity, refresh) along with other data.

The access token can then be used in subsequent API calls to IdentityServer:

HttpClient _httpClient;

_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri(Constants.ApiUri);
_httpClient.DefaultRequestHeaders.Authorization
    = new AuthenticationHeaderValue("Bearer", _loginResult?.AccessToken ?? string.Empty);

HttpResponseMessage response = await _httpClient.GetAsync("test");
string content = await response.Content.ReadAsStringAsync();

if (response.IsSuccessStatusCode)
{
    editor.Text = JArray.Parse(content).ToString();
}
else
{
    editor.Text = response.ReasonPhrase;
}

Here, the access token is retrieved from the LoginResult object and set as the bearer token in the Authorization header of a HttpClient object: The HttpClient object then makes a call to a specific IdentityServer API, using the access token for authorization.

Wrapping up

This blog post has shown how to combine the Xamarin.Essentials WebAuthenticator class with the IdentityModel.OidcClient library to initiate authentication flows, and process the response. The advantage of combining WebAuthenticator with OidcClient is that (1) you don’t have to write any platform code to invoke native browsers, and (2) you don’t need to write any code to generate the correct authentication query parameters. In addition, because OidcClient is OpenID certified, you have peace of mind that it’s correctly implemented the OAuth 2.0 spec for native apps. For more information about the IdentityModel.OidcClient library, see Building mobile/native clients in the IdentityModel docs.

The sample this code comes from can be found on GitHub.

Tuesday, 7 April 2020

Authentication from a Xamarin.Forms app using WebAuthenticator

The OAuth 2.0 for Native Apps spec represents the best practices for OAuth 2.0 authentication flows from mobile apps. These include:
  • Authentication requests should only be made through external user agents, such as the browser. This results in better security, and enables use of the user’s current authentication state, making single sign-on possible. Conversely, this means that authentication requests should never be made through a WebView. WebView controls are unsafe for third parties, as they leave the authorization grant and user’s credentials vulnerable to recording or malicious use. In addition, WebView controls don’t share authentication state, meaning single sign-on isn’t possible.
  • Native apps must request user authorization by creating a URI with the appropriate grant types. The app then redirects the user to this request URI. A redirect URI that the native app can receive and parse must also be supplied.
  • Native apps must use the Proof Key for Code Exchange (PKCE) protocol, to defend against apps on the same device potentially intercepting the authorization code.
  • Native apps should use the authorization code grant flow with PKCE. Conversely, native apps shouldn’t use the implicit grant flow.
  • Cross-Site Request Forgery (CSRF) attacks should be migitated by using the state parameter to link requests and responses.
Ultimately, the OAuth 2.0 authentication flow for native apps becomes:
  1. The native app opens a browser tab with the authorisation request.
  2. The authorisation endpoint receives the authorisation request, authenticates the user, and obtains authorisation.
  3. The authorisation server issues an authoration code to the redirect URI.
  4. The native app receives the authorisation code from the redirect URI.
  5. The native app presents the authorisation code at the token endpoint.
  6. The token endpoint validates the authorization code and issues the requested tokens.
I’d previously written a Xamarin.Forms sample to do this without any additional libraries, and one with the IdentityModel.OidcClient2 library, which is OpenID certified. Both samples consume endpoints on a publically available IdentityServer site, and use platform code to invoke native browsers.

Enter Xamarin.Essentials


Xamarin.Essentials recently introduced a WebAuthenticator class, which is a web navigation API that can be used for authentication with web services. The class contains a single method, AuthenticateAsync, which starts an authentication flow by navigating to a specified URI, and then waits for a callback/redirect to the redirect URI scheme. The whole point of the class is that it avoids having to write platform code to invoke native browsers. For more information, see the WebAuthenticator doc.

I decided to test drive WebAuthenticator by using it to initate an authentication flow with IdentityServer. However, an authentication flow from a mobile app should include a web backend between the mobile app and the authentication provider. This is to avoid including client secrets in your mobile app, which is considered to be insecure. However, I’ve avoided a web backend as middleware here, as I just want to see what’s involved in authenticating with IdentityServer using the WebAuthenticator class.

However, before you can use WebAuthenticator to authenticate with IdentityServer, you first have to perform some platform specific setup.

Platform setup


On Android, you require an IntentFilter to handle your redirect URI. This can be accomplished by subclassing the Xamarin.Essentials WebAuthenticatorCallbackActivity class:
[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop)]
[IntentFilter(new[] { Intent.ActionView},
    Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable},
    DataScheme = "io.identitymodel.native",
    DataHost = "callback")]
public class WebAuthenticationCallbackActivity : WebAuthenticatorCallbackActivity
{
}
You’ll also need to overide the OnResume method in your MainActivity class, to call back into Xamarin.Essentials:
protected override void OnResume()
{
    base.OnResume();
    Xamarin.Essentials.Platform.OnResume();
}
On iOS, you’ll need to add your app’s redirect URI to your Info.plist file:
 <key>CFBundleURLTypes</key>
 <array>
  <dict>
   <key>CFBundleURLName</key>
   <string>WebAuthenticatorDemo</string>
   <key>CFBundleTypeRole</key>
   <string>None</string>
   <key>CFBundleURLSchemes</key>
   <array>
    <string>io.identitymodel.native</string>
   </array>
  </dict>
 </array>
You’ll also need to overide the OpenUrl method in your AppDelegate class, to call back into Xamarin.Essentials:
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
    if (Xamarin.Essentials.Platform.OpenUrl(app, url, options))
        return true;

    return base.OpenUrl(app, url, options);
}
On UWP, you need to declare your redirect URI in your Package.appxmanifest file:
    <Applications>
      <Application Id="App"
        Executable="$targetnametoken$.exe"
        EntryPoint="WebAuthenticatorDemo.UWP.App">
        ...

        <Extensions>
          <uap:Extension Category="windows.protocol">
            <uap:Protocol Name="io.identitymodel.native">
              <uap:DisplayName>WebAuthenticatorDemo</uap:DisplayName>
            </uap:Protocol>
          </uap:Extension>
        </Extensions>
      
      </Application>
    </Applications>

Start an authentication flow with WebAuthenticator


An authentication flow with IdentityServer can be started by creating the start URI, and passing it to the AuthenticateAsync method along with the redirect URI:
string url = identityService.CreateAuthorizationRequest();
var authResult = await WebAuthenticator.AuthenticateAsync(new Uri(url), new Uri(Constants.RedirectUri));
If you aren’t familiar with the process of building an authorization request to IdentityServer, I recommend you look at the IdentityService class in the sample. It involves creating a long query string that contains data that indicates to IdentityServer what kind of authentication flow to kick off.

The AuthenticateAsync method returns a WebAuthenticatorResult object, which contains a number of properties, such as AccessToken, and RefreshToken. More importantly, it has a Properties dictionary that contains all of the query string or URI fragment properties, that can then be accessed by their key. Therefore, the Properties dictionary can be parsed to build the raw response sent by IdentityServer:
string raw = ParseAuthenticatorResult(authResult);
authorizeResponse = new AuthorizeResponse(raw);

string ParseAuthenticatorResult(WebAuthenticatorResult result)
{
    string code = result?.Properties["code"];
    string idToken = result?.IdToken;
    string scope = result?.Properties["scope"];
    string state = result?.Properties["state"];
    string sessionState = result?.Properties["session_state"];
    return $"{Constants.RedirectUri}#code={code}&id_token={idToken}&scope={scope}&state={state}&session_state={sessionState}";
}
The result of a successful authentication flow is that the AuthorizeResponse object contains an authorization code, that can then be exchanged for the requested tokens:
UserToken userToken = await identityService.GetTokenAsync(authorizeResponse.Code);
If the UserToken object contains an access token, it can be used when making API calls to IdentityServer:
var content = await identityService.GetAsync($"{Constants.ApiUri}test", userToken.AccessToken);

Wrapping up


Although there's a lot going on here, in terms of generating the start URI, and processing the response from the redirect URI, using the WebAuthenticator class has avoided having to write platform code to invoke native browsers. This can be a considerable amount of work, depending on the platform, and it can also be confusing if you aren't familiar with the intricacies of each platform.

I've deliberately left out a lot of code from this blog post, which generates the queries to send to IdentityServer. This is because my aim wasn't to cover the intricacies of IdentityServer, but instead how to initiate an authentication flow with WebAuthenticator, and how to process its response. I also said that all of this is simple, but it’s only simple if you have a good understanding of the OAuth 2.0 spec. If you don’t, it can be overwhelming. Therefore, in my next blog post I’ll look at simplifying all of this by using the IdentityModel.OidcClient library.

The sample this code comes from can be found on GitHub.