Authentifiez vos applications Xamarin avec votre compte Microsoft

Connectez-vous avec votre compte Microsoft dans vos applications Xamarin !

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

Lors du développement d’une application mobile, vous avez souvent besoin d’un système d’authentification pour vos utilisateurs. Créer un nouveau compte pour un usage spécifique, cela peut constituer un frein pour certains utilisateurs. Vos utilisateurs auront probablement déjà un compte Google, Facebook, Github ou Microsoft. Alors pourquoi ne pas utiliser l’un d’eux au lieu de multiplier le nombre de comptes ?

Dans ce tutoriel, je vais vous montrer comment authentifier vos applications Xamarin à l’aide du système d’authentification Microsoft.

Voici quelques avantages de l’utilisation de cette technique :

  • Vous n’avez pas à gérer les pages d’inscription et de connexion pour votre application
  • L’authentification est déléguée à un service tiers
  • Avec votre compte Microsoft, vous pouvez obtenir un IdToken à partir de la connexion associée et l’utiliser avec Open Id Connect pour autoriser l’accès à une API Web.

Configurez votre application à l’aide du portail Azure

Avant de pouvoir connecter vos utilisateurs à leur compte Microsoft, nous devons enregistrer une application sur le Portail Azure. Cette application est utilisée pour déclarer le type de compte d’utilisateur autorisé à se connecter avec son compte Microsoft dans votre application.

Commençons donc par vous rendre sur le site Web Azure Portal. Dans le menu de gauche, sélectionnez Azure Active Directory, puis App registratons, puis cliquez sur New registration en haut de l’écran.

Ensuite :

  • Donnez un nom à votre application, pour ce tutoriel je l’appellerai MicrosoftAuthDemo
  • Assurez-vous que le type de compte pris en charge est défini sur Accounts in any organizational directory and personal Microsoft accounts (e.g. Skype, Xbox, Outlook.com) afin que tous les utilisateurs ayant un compte Microsoft puissent se connecter à votre application.
  • Enfin, cliquez sur le bouton Register en bas de la page.

Register application

Maintenant, si vous ouvrez votre application, dans l’onglet Overview, vous trouverez votre Application (client) ID. Cet identifiant d’application sera utilisé pour identifier et autoriser votre application Xamarin, qui est obligatoire pour authentifier les utilisateurs à l’aide de leur compte Microsoft.

Application Xamarin

Nous avons maintenant la configuration de notre application Azure, passons à l’étape suivante: intégrer l’authentification Microsoft dans notre application Xamarin. Tout d’abord, installons le package Nuget Microsoft.Identity.Client à votre code partagé et vos projets de chaque plateformes.

Service d’authentification Microsoft

Créons une nouvelle classe appelée MicrosoftAuthService où nous allons implémenter 4 méthodes :

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;
    }
}

Expliquons cette classe :

  • Nous devons d’abord déclarer le ClientID que nous avons récupéré du portail Azure. Ensuite, la méthode Initialize initialisera le PublicClientApplication avec notre Application (client) ID. Pour iOS, nous devons appeler la méthode WithIosKeychainSecurityGroup pour permettre à l’application d’accéder au trousseau (Keychain) de l’appareil.

  • La méthode OnSignInAsync affichera automatiquement la Webview d’authentification Microsoft. Nous vérifions d’abord si un compte est déjà présent sur l’appareil, si c’est le cas, le compte sera automatiquement proposé à l’utilisateur. Nous passons un Scope qui représente la liste des éléments auxquels nous souhaitons avoir accès. Ici, nous voulons seulement lire les informations de l’utilisateur donc nous utilisions: User.Read

  • La méthode OnSignOutAsync supprime tous les comptes connectés sur l’appareil.

  • Enfin, RefreshUserDataAsync appellera la Graph Api de Microsoft pour obtenir des détails sur le compte d’utilisateur.

Je vous recommanderai de créer l’interface associée à ce service pour pouvoir utiliser l’injection de dépendance dans un projet réel.

Configurer le projet Android

Pour Android, vous devez suivre les 3 étapes suivantes :

Tout d’abord, allez dans votre MainActivity.cs et overridez la méthode OnActivityResult et ajoutez-y cette ligne :

AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(requestCode, resultCode, data);

Cela garantira que le contrôle reviendra à MSAL (Microsoft Authentication Library) lorsque la partie interactive du flux d’authentification sera terminée.

Seconde étape, ajoutez cette ligne à la fin de votre méthode OnCreate, cela garantira que les flux d’authentification se produisent dans le contexte de l’activité en cours.

MicrosoftAuthService.ParentWindow = this;

Dernière étape pour Android, allez dans le fichier AndroidManifest.xml et déclarez une nouvelle activité comme ceci:

<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>

Cette activité est utilisée pour afficher la page d’authentification Microsoft dans une activité spécifique. Assurez-vous de configurer correctement l’Application (client) ID.

Configurer le projet iOS

Pour le projet iOS, vous devez suivre ces 3 étapes :

Tout d’abord, allez dans la classe AppDelegate.cs et overridez la méthode suivante :

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

Comme nous l’avons fait dans le projet Android, cette ligne assurera le bon fonctionnement de MSAL.

La prochaine étape consiste à activer les Keychain Groups dans le fichier Entitlements.plist et à spécifier un KeyChain Group avec com.microsoft.adalcache afin de permettre à l’application d’accéder au trousseau de l’appareil.

Keychain Group

Ouvrez ensuite les paramètres du projet et dans iOS Bundle Signing, sélectionnez Entitlements.plist dans la propriété Custom Entitlements.

Si vous ne le faites pas correctement, vous obtiendrez ce genre d’erreur:

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.

Dernière étape, allez dans le fichier Info.plist et ajoutez ces lignes :

<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>

Ceci sera utilisé lorsque l’application ouvrira l’URL d’authentification MSAL. N’oubliez pas de configurer correctement l’Application (client) ID et de changer le CFBundleURLName avec le Bundle identifier de votre projet.

Touche finale

J’ai créé un exemple simple et complet de ce code en utilisant l’injection de dépendance avec MvvmLightLibs pour vous montrer un exemple complet avec une architecture simple.

Vous trouverez un exemple de code sur ce répertoire Github.

Happy codding !

Vous avez aimé ce tutoriel ? Laissez une étoile sur le répertoire Github associé !

N'hésitez pas à me suivre sur pour ne pas rater mon prochain tutoriel !