Authenticate your Xamarin applications using your Microsoft account

Sign in with your Microsoft account in your Xamarin applications!

Posted by Damien Aicheh on July 01, 2019 · 13 mins

When developing a mobile application you often need an authentication system for your users. Create a new account for a specific usage this can be a barrier for some users. Your users will probably already have a Google, Facebook, Github or a Microsoft account. So why not using one of them instead of multiply the number of accounts ?

In this tutorial I’m going to show you how to authenticate your Xamarin applications using the Microsoft authentication system.

Here some advantages of using this technique:

  • You don’t have to manage the sign up and sign in pages for your application
  • The authentication is delegated to a third party service
  • With your Microsoft account, you can get an IdToken from the associated connection and use it with Open Id connect to authorize the access to a Web API

Configure your application using the Azure Portal

Before being able to connect your users with their Microsoft account, we need to register an application on the Azure Portal. This application is used to declare the kind of users account authorized to sign in with their Microsoft Account in your application.

So let’s start by going to the Azure Portal website, inside the left menu select Azure Active Directory then App registratons and click on New registration at the top of the screen.

Then :

  • Set a name to your application, for this tutorial I will call it MicrosoftAuthDemo
  • Be sure the supported account type is set to Accounts in any organizational directory and personal Microsoft accounts (e.g. Skype, Xbox, Outlook.com) so all users with a Microsoft account can sign in to your application.
  • Finally, click the Register button at the bottom of the page.

Register application

Now if you open your application, inside the Overview tab you will find your Application (client) ID. This application id will be used to identify and authorize your Xamarin application, which is mandatory to authenticate the users using their Microsoft account.

Xamarin application

We have our Azure application setup now, let’s move on to the next step : integrate the Microsoft authentication in our Xamarin application. First of all, install the Microsoft.Identity.Client Nuget to your shared code and your platforms projects.

Microsoft Authentication Service

Let’s create a new class called MicrosoftAuthService where we will implements 4 methods:

public class MicrosoftAuthService : IMicrosoftAuthService
{
    private readonly string ClientID = "REPLACE_WITH_YOUR_APP_ID";
    private readonly string[] Scopes = { "User.Read" };
    private readonly string GraphUrl = "https://graph.microsoft.com/v1.0/me";

    private IPublicClientApplication publicClientApplication;

    public void Initialize()
    {
        this.publicClientApplication = PublicClientApplicationBuilder.Create(ClientID)
            .WithIosKeychainSecurityGroup("com.microsoft.adalcache")
            .WithRedirectUri($"msal{ClientID}://auth")
            .Build();
    }

    /// <summary>
    /// This object is used to know where to display the authentication activity (for Android) or page.
    /// </summary>
    public static object ParentWindow { get; set; }

    /// <summary>
    /// Signin with your Microsoft account.
    /// </summary>
    public async Task<User> OnSignInAsync()
    {
        User currentUser = null;

        var accounts = await this.publicClientApplication.GetAccountsAsync();
        try
        {
            try
            {
                var firstAccount = accounts.FirstOrDefault();
                var authResult = await this.publicClientApplication.AcquireTokenSilent(Scopes, firstAccount).ExecuteAsync();
                currentUser = await this.RefreshUserDataAsync(authResult?.AccessToken).ConfigureAwait(false);
            }
            catch (MsalUiRequiredException ex)
            {
                // the user was not already connected.
                try
                {
                    var authResult = await this.publicClientApplication.AcquireTokenInteractive(Scopes)
                                                .WithParentActivityOrWindow(ParentWindow)
                                                .ExecuteAsync();
                    currentUser = await this.RefreshUserDataAsync(authResult?.AccessToken).ConfigureAwait(false);
                }
                catch (Exception ex2)
                {
                    // Manage the exception with a logger as you need
                    System.Diagnostics.Debug.WriteLine(ex2.ToString());
                }
            }
        }
        catch (Exception ex)
        {
            // Manage the exception with a logger as you need
            System.Diagnostics.Debug.WriteLine(ex.ToString());
        }

        return currentUser;
    }

    /// <summary>
    /// Sign out with your Microsoft account.
    /// </summary>
    public async Task OnSignOutAsync()
    {
        var accounts = await this.publicClientApplication.GetAccountsAsync();
        try
        {
            while (accounts.Any())
            {
                await this.publicClientApplication.RemoveAsync(accounts.FirstOrDefault());
                accounts = await this.publicClientApplication.GetAccountsAsync();
            }
        }
        catch (Exception ex)
        {
            // Manage the exception with a logger as you need
            System.Diagnostics.Debug.WriteLine(ex.ToString());
        }
    }

    /// <summary>
    /// Refresh user date from the Graph api.
    /// </summary>
    /// <param name="token">The user access token.</param>
    /// <returns>The current user with his associated informations.</returns>
    private async Task<User> RefreshUserDataAsync(string token)
    {
        HttpClient client = new HttpClient();
        HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, this.GraphUrl);
        message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);
        HttpResponseMessage response = await client.SendAsync(message);
        User currentUser = null;

        if (response.IsSuccessStatusCode)
        {
            string json = await response.Content.ReadAsStringAsync();
            currentUser = JsonConvert.DeserializeObject<User>(json);
        }

        return currentUser;
    }
}

Let’s explain this class:

  • First we need to declare the ClientID that we retrieved from the Azure Portal. Then the Initialize method will init the PublicClientApplication with our Application (client) ID. For iOS we need to call the WithIosKeychainSecurityGroup method to let the application access to the Keychain of the device.

  • The OnSignInAsync method will automatically display the Microsoft authentication webview. We first check if an account is already present on the device if it’s the case, the account will be automatically suggested to the user. We pass a Scope that represent the list of elements we want to get access, here we just want to read the user informations so we use : User.Read

  • The OnSignOutAsync method remove all accounts connected on the device.

  • Finally the RefreshUserDataAsync will call the Graph Api from Microsoft to get the details about the user account.

I will recommend you to create the associated interface for this service to be able to use dependency injection in a real project.

Setup the Android project

For Android you need to do the 3 following steps:

First one, go to your MainActivity.cs and override the OnActivityResult method and add this line to it:

AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(requestCode, resultCode, data);

This will ensure that the control goes back to MSAL (Microsoft Authentication Library) when the interactive portion of the authentication flow will be finished.

Second step, add this line to the end of your OnCreate method, this will ensure that the authentication flows occur in the context of the current activity.

MicrosoftAuthService.ParentWindow = this;

Last step for Android, go to the AndroidManifest.xml and declare a new activity like this:

<application android:label="MicrosoftDemoProject.Android">
    <activity android:name="microsoft.identity.client.BrowserTabActivity">
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="msalREPLACE_WITH_YOUR_APP_ID" android:host="auth" />
        </intent-filter>
    </activity>
</application>

This activity is used to display the Microsoft authentication page in a specific activity. Be sure to setup the Application (client) ID correctly in it.

Setup the iOS project

For the iOS project you need to follow these 3 steps:

First of all go to the AppDelegate.cs class and override the following method:

public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
    AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(url);
    return true;
}

Like we did in the Android project, this line will ensure the proper functioning of MSAL.

Next step we need to enable the Keychain Groups inside the Entitlements.plist and specify a KeyChain Group with com.microsoft.adalcache this will allow the application to access the Keychain of the device.

Keychain Group

Then open the project settings and in the iOS Bundle Signing select the Entitlements.plist inside the Custom Entitlements property.

If you don’t do that correctly you will get this kind of error:

Microsoft.Identity.Client.MsalClientException: The application cannot access the iOS keychain for the application publisher (the TeamId is null). This is needed to enable Single Sign On between applications of the same publisher. This is an iOS configuration issue. See https://aka.ms/msal-net-enable-keychain-access for more details on enabling keychain access.

Final step go to the Info.plist and add these lines:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLName</key>
        <string>com.companyname.MicrosoftDemoProject</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>msalREPLACE_WITH_YOUR_APP_ID</string>
        </array>
    </dict>
</array>

This will used when the application will open the MSAL authentication URL. Don’t forget to setup the Application (client) ID correctly in it and change the CFBundleURLName with the Bundle identifier of your project.

Final touch

I created a complete and simple example of this code using dependency injection with MvvmLightLibs to show you a complete example with a simple architecture.

You will find full source code in this Github repository.

Happy coding !

You liked this tutorial ? Leave a star in the associated Github repository !

Do not hesitate to follow me on to not miss my next tutorial!