Partager via


Créer une application de connexion Windows Hello

Il s’agit de la première partie d’une procédure pas à pas complète sur la création d’une application Windows empaquetée qui utilise Windows Hello comme alternative aux systèmes d’authentification de nom d’utilisateur et de mot de passe traditionnels. Dans ce cas, l’application est une application WinUI, mais la même approche peut être utilisée avec n’importe quelle application Windows empaquetée, y compris les applications WPF et Windows Forms. L’application utilise un nom d’utilisateur pour la connexion et crée une clé Hello pour chaque compte. Ces comptes sont protégés par le code confidentiel configuré dans les paramètres Windows lors de la configuration de Windows Hello.

Cette procédure pas à pas est divisée en deux parties : la création de l’application et la connexion du service principal. Une fois cet article terminé, passez à la partie 2 : Service de connexion Windows Hello.

Avant de commencer, vous devez lire la vue d’ensemble de Windows Hello pour une compréhension générale du fonctionnement de Windows Hello .

Bien démarrer

Pour générer ce projet, vous aurez besoin d’une expérience avec C# et XAML. Vous devez également utiliser Visual Studio 2022 sur un ordinateur Windows 10 ou Windows 11. Consultez Prise en main de WinUI pour obtenir des instructions complètes sur la configuration de votre environnement de développement.

  • Dans Visual Studio, sélectionnez Fichier>Nouveau>Projet.
  • Dans les filtres déroulants de la boîte de dialogue Nouveau projet , sélectionnez C#/C++, Windows et WinUI, respectivement.
  • Choisissez Application vide, Empaquetée (WinUI 3 dans Desktop) et nommez votre application « WindowsHelloLogin ».
  • Générez et exécutez la nouvelle application (F5), une fenêtre vide doit s’afficher à l’écran. Fermez l’application.

Capture d’écran de la nouvelle application de connexion Windows Hello en cours d’exécution pour la première fois

Exercice 1 : Connexion avec Windows Hello

Dans cet exercice, vous allez apprendre à vérifier si Windows Hello est configuré sur l’ordinateur et comment vous connecter à un compte à l’aide de Windows Hello.

  • Dans le nouveau projet, créez un dossier dans la solution appelée « Vues ». Ce dossier contient les pages qui seront redirigées vers cet exemple. Cliquez avec le bouton droit sur le projet dans Explorateur de solutions, sélectionnez Ajouter>un nouveau dossier, puis renommez le dossier en affichages.

    Capture d’écran de l’ajout d’un nouveau dossier nommé Views au projet de connexion Windows Hello

  • Ouvrez MainWindow.xaml et remplacez le Window contenu par un contrôle ou Grid videStackPanel. Nous allons implémenter la navigation de page et accéder à une nouvelle page lorsque MainWindow est chargé. Nous n’avons donc pas besoin de contenu dans MainWindow.

  • Ajoutez une Title propriété à MainWindow dans le code XAML. L’attribut doit ressembler à ceci : Title="Windows Hello Login".

  • Supprimez le gestionnaire d’événements myButton_Click de MainWindow.xaml.cs pour éviter toute erreur de compilation. Ce gestionnaire d’événements n’est pas nécessaire pour cet exemple.

  • Cliquez avec le bouton droit sur le nouveau dossier Affichages, sélectionnez Ajouter>un nouvel élément et sélectionnez le modèle Page vide. Nommez cette page « MainPage.xaml ».

    Capture d’écran de l’ajout d’une nouvelle page vide au projet de connexion Windows Hello

  • Ouvrez le fichier App.xaml.cs et mettez à jour le gestionnaire OnLaunched pour implémenter la navigation de page pour l’application. Vous devez également ajouter une méthode de gestionnaire de RootFrame_NavigationFailed pour gérer les erreurs qui se produisent lors du chargement des pages.

    protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
    {
        m_window = new MainWindow();
        var rootFrame = new Frame();
        rootFrame.NavigationFailed += RootFrame_NavigationFailed;
        rootFrame.Navigate(typeof(MainPage), args);
        m_window.Content = rootFrame;
        m_window.Activate();
    }
    
    private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
    {
        throw new Exception($"Error loading page {e.SourcePageType.FullName}");
    }
    
  • Vous devez également ajouter quatre instructions using en haut du fichier App.xaml.cs pour résoudre les erreurs de compilation dans le code.

    using Microsoft.UI.Xaml.Controls;
    using Microsoft.UI.Xaml.Navigation;
    using System;
    using WindowsHelloLogin.Views;
    
  • Cliquez avec le bouton droit sur le nouveau dossier Affichages, sélectionnez Ajouter>un nouvel élément et sélectionnez le modèle Page vide. Nommez cette page « Login.xaml ».

  • Pour définir l’interface utilisateur de la nouvelle page de connexion, ajoutez le code XAML suivant. Ce code XAML définit un StackPanel code pour aligner les enfants suivants :

    • Qui TextBlock contiendra un titre.

    • Pour TextBlock les messages d’erreur.

    • Pour TextBox que le nom d’utilisateur soit entré.

    • Pour Button accéder à une page d’inscription.

    • À TextBlock contenir l’état de Windows Hello.

    • Pour TextBlock expliquer la page de connexion, car il n’existe pas encore d’utilisateurs back-end ou configurés.

      <Grid>
        <StackPanel>
          <TextBlock Text="Login" FontSize="36" Margin="4" TextAlignment="Center"/>
          <TextBlock x:Name="ErrorMessage" Text="" FontSize="20" Margin="4" Foreground="Red" TextAlignment="Center"/>
          <TextBlock Text="Enter your username below" Margin="0,0,0,20"
                     TextWrapping="Wrap" Width="300"
                     TextAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
          <TextBox x:Name="UsernameTextBox" Margin="4" Width="250"/>
          <Button x:Name="LoginButton" Content="Login" Background="DodgerBlue" Foreground="White"
                  Click="LoginButton_Click" Width="80" HorizontalAlignment="Center" Margin="0,20"/>
          <TextBlock Text="Don't have an account?"
                     TextAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
          <TextBlock x:Name="RegisterButtonTextBlock" Text="Register now"
                     PointerPressed="RegisterButtonTextBlock_OnPointerPressed"
                     Foreground="DodgerBlue"
                     TextAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
          <Border x:Name="WindowsHelloStatus" Background="#22B14C"
                  Margin="0,20" Height="100" >
            <TextBlock x:Name="WindowsHelloStatusText" Text="Windows Hello is ready to use!"
                       Margin="4" TextAlignment="Center" VerticalAlignment="Center" FontSize="20"/>
          </Border>
          <TextBlock x:Name="LoginExplanation" FontSize="24" TextAlignment="Center" TextWrapping="Wrap" 
                     Text="Please Note: To demonstrate a login, validation will only occur using the default username 'sampleUsername'"/>
        </StackPanel>
      </Grid>
      
  • Quelques méthodes doivent être ajoutées au fichier code-behind pour obtenir la création de la solution. Appuyez sur F7 ou utilisez le Explorateur de solutions pour modifier le fichier Login.xaml.cs. Ajoutez les deux méthodes d’événement suivantes pour gérer les événements Login et Register . Pour l’instant, ces méthodes définissent la ErrorMessage.Text valeur sur une chaîne vide. Veillez à inclure les instructions using suivantes. Ils seront nécessaires pour les étapes suivantes.

    using Microsoft.UI.Xaml;
    using Microsoft.UI.Xaml.Controls;
    using Microsoft.UI.Xaml.Input;
    using Microsoft.UI.Xaml.Media;
    using Microsoft.UI.Xaml.Navigation;
    using System;
    
    namespace WindowsHelloLogin.Views
    {
        public sealed partial class Login : Page
        {
            public Login()
            {
                this.InitializeComponent();
            }
            private void LoginButton_Click(object sender, RoutedEventArgs e)
            {
                ErrorMessage.Text = "";
            }
            private void RegisterButtonTextBlock_OnPointerPressed(object sender, PointerRoutedEventArgs e)
            {
                ErrorMessage.Text = "";
            }
        }
    }
    
  • Pour afficher la page De connexion, modifiez le code MainPage pour accéder à la page de connexion lorsque mainPage est chargé. Ouvrez le fichier MainPage.xaml.cs. Dans Explorateur de solutions, double-cliquez sur MainPage.xaml.cs. Si vous ne trouvez pas ce bouton, cliquez sur la petite flèche en regard de MainPage.xaml pour afficher le fichier code-behind. Créez une méthode de gestionnaire d’événements Loaded qui accède à la page Connexion .

    namespace WindowsHelloLogin.Views
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
                Loaded += MainPage_Loaded;
            }
    
            private void MainPage_Loaded(object sender, RoutedEventArgs e)
            {
                Frame.Navigate(typeof(Login));
            }
        }
    }
    
  • Dans la page Connexion , vous devez gérer l’événement OnNavigatedTo pour vérifier si Windows Hello est disponible sur l’ordinateur actuel. Dans Login.xaml.cs, implémentez le code suivant. Vous remarquerez que l’objet WindowsHelloHelper indique qu’il existe une erreur. C’est parce que nous n’avons pas encore créé cette classe d’assistance.

    public sealed partial class Login : Page
    {
        public Login()
        {
            this.InitializeComponent();
        }
    
        protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
            // Check if Windows Hello is set up and available on this machine
            if (await WindowsHelloHelper.WindowsHelloAvailableCheckAsync())
            {
            }
            else
            {
                // Windows Hello isn't set up, so inform the user
                WindowsHelloStatus.Background = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 50, 170, 207));
                WindowsHelloStatusText.Text = $"Windows Hello is not set up!{Environment.NewLine}Please go to Windows Settings and set up a PIN to use it.";
                LoginButton.IsEnabled = false;
            }
        }
    }
    
  • Pour créer la classe WindowsHelloHelper, cliquez avec le bouton droit sur le projet WindowsHelloLogin , puis cliquez sur Ajouter>un nouveau dossier. Nommez ce dossier Utils.

  • Cliquez avec le bouton droit sur le dossier Utils et sélectionnez Ajouter une>classe. Nommez cette nouvelle classe « WindowsHelloHelper.cs ».

    Capture d’écran de la création de la classe d’assistance de connexion Windows Hello

  • Modifiez l’étendue de la classe public staticWindowsHelloHelper, puis ajoutez la méthode suivante pour informer l’utilisateur si Windows Hello est prêt à être utilisé ou non. Vous devez ajouter les espaces de noms requis.

    using System;
    using System.Diagnostics;
    using System.Threading.Tasks;
    using Windows.Security.Credentials;
    
    namespace WindowsHelloLogin.Utils
    {
        public static class WindowsHelloHelper
        {
            /// <summary>
            /// Checks to see if Windows Hello is ready to be used.
            /// 
            /// Windows Hello has dependencies on:
            ///     1. Having a connected Microsoft Account
            ///     2. Having a Windows PIN set up for that account on the local machine
            /// </summary>
            public static async Task<bool> WindowsHelloAvailableCheckAsync()
            {
                bool keyCredentialAvailable = await KeyCredentialManager.IsSupportedAsync();
                if (keyCredentialAvailable == false)
                {
                    // Key credential is not enabled yet as user 
                    // needs to connect to a Microsoft Account and select a PIN in the connecting flow.
                    Debug.WriteLine("Windows Hello is not set up!\nPlease go to Windows Settings and set up a PIN to use it.");
                    return false;
                }
    
                return true;
            }
        }
    }
    
  • Dans Login.xaml.cs, ajoutez une référence à l’espace WindowsHelloLogin.Utils de noms. Cela permet de résoudre l’erreur dans la OnNavigatedTo méthode.

    using WindowsHelloLogin.Utils;
    
  • Générez et exécutez l’application. Vous êtes redirigé vers la page de connexion et la bannière Windows Hello vous indique si Windows Hello est prêt à être utilisé. Vous devez voir la bannière verte ou bleue indiquant l’état Windows Hello sur votre ordinateur.

    Capture d’écran de l’écran de connexion Windows Hello avec un état prêt

  • La prochaine chose que vous devez faire est de générer la logique de connexion. Créez un dossier dans le projet nommé « Models ».

  • Dans le dossier Models , créez une classe appelée « Account.cs ». Cette classe agit comme modèle de compte. Comme il s’agit d’un exemple de projet, il ne contient qu’un nom d’utilisateur. Modifiez l’étendue de la classe et public ajoutez la Username propriété.

    namespace WindowsHelloLogin.Models
    {
        public class Account
        {
            public string Username { get; set; }
        }
    }
    
  • L’application a besoin d’un moyen de gérer les comptes. Pour ce labo pratique, car il n’existe aucun serveur ou base de données, une liste d’utilisateurs est enregistrée et chargée localement. Cliquez avec le bouton droit sur le dossier Utils et ajoutez une nouvelle classe nommée « AccountHelper.cs ». Modifiez l’étendue de la classe pour qu’elle soit public static. AccountHelper est une classe statique qui contient toutes les méthodes nécessaires pour enregistrer et charger la liste des comptes localement. L’enregistrement et le chargement fonctionnent à l’aide d’un xmlSerializer. Vous devez également mémoriser le fichier enregistré et l’emplacement où vous l’avez enregistré.

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.Threading.Tasks;
    using System.Xml.Serialization;
    using Windows.Storage;
    using WindowsHelloLogin.Models;
    
    namespace WindowsHelloLogin.Utils
    {
        public static class AccountHelper
        {
            // In the real world this would not be needed as there would be a server implemented that would host a user account database.
            // For this tutorial we will just be storing accounts locally.
            private const string USER_ACCOUNT_LIST_FILE_NAME = "accountlist.txt";
            private static string _accountListPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, USER_ACCOUNT_LIST_FILE_NAME);
            public static List<Account> AccountList = [];
    
            /// <summary>
            /// Create and save a useraccount list file. (Updating the old one)
            /// </summary>
            private static async void SaveAccountListAsync()
            {
                string accountsXml = SerializeAccountListToXml();
    
                if (File.Exists(_accountListPath))
                {
                    StorageFile accountsFile = await StorageFile.GetFileFromPathAsync(_accountListPath);
                    await FileIO.WriteTextAsync(accountsFile, accountsXml);
                }
                else
                {
                    StorageFile accountsFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(USER_ACCOUNT_LIST_FILE_NAME);
                    await FileIO.WriteTextAsync(accountsFile, accountsXml);
                }
            }
    
            /// <summary>
            /// Gets the useraccount list file and deserializes it from XML to a list of useraccount objects.
            /// </summary>
            /// <returns>List of useraccount objects</returns>
            public static async Task<List<Account>> LoadAccountListAsync()
            {
                if (File.Exists(_accountListPath))
                {
                    StorageFile accountsFile = await StorageFile.GetFileFromPathAsync(_accountListPath);
    
                    string accountsXml = await FileIO.ReadTextAsync(accountsFile);
                    DeserializeXmlToAccountList(accountsXml);
                }
    
                return AccountList;
            }
    
            /// <summary>
            /// Uses the local list of accounts and returns an XML formatted string representing the list
            /// </summary>
            /// <returns>XML formatted list of accounts</returns>
            public static string SerializeAccountListToXml()
            {
                var xmlizer = new XmlSerializer(typeof(List<Account>));
                var writer = new StringWriter();
                xmlizer.Serialize(writer, AccountList);
    
                return writer.ToString();
            }
    
            /// <summary>
            /// Takes an XML formatted string representing a list of accounts and returns a list object of accounts
            /// </summary>
            /// <param name="listAsXml">XML formatted list of accounts</param>
            /// <returns>List object of accounts</returns>
            public static List<Account> DeserializeXmlToAccountList(string listAsXml)
            {
                var xmlizer = new XmlSerializer(typeof(List<Account>));
                TextReader textreader = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(listAsXml)));
    
                return AccountList = (xmlizer.Deserialize(textreader)) as List<Account>;
            }
        }
    }
    
  • Ensuite, implémentez un moyen d’ajouter et de supprimer un compte de la liste locale des comptes. Ces actions enregistrent chacune la liste. La méthode finale dont vous aurez besoin pour ce laboratoire pratique est une méthode de validation. Comme il n’existe pas de serveur d’autorisation ou de base de données d’utilisateurs, cette opération est validée par rapport à un seul utilisateur codé en dur. Ces méthodes doivent être ajoutées à la classe AccountHelper .

    public static Account AddAccount(string username)
    {
        // Create a new account with the username
        var account = new Account() { Username = username };
        // Add it to the local list of accounts
        AccountList.Add(account);
        // SaveAccountList and return the account
        SaveAccountListAsync();
        return account;
    }
    
    public static void RemoveAccount(Account account)
    {
        // Remove the account from the accounts list
        AccountList.Remove(account);
        // Re save the updated list
        SaveAccountListAsync();
    }
    
    public static bool ValidateAccountCredentials(string username)
    {
        // In the real world, this method would call the server to authenticate that the account exists and is valid.
        // However, for this tutorial, we'll just have an existing sample user that's named "sampleUsername".
        // If the username is null or does not match "sampleUsername" validation will fail. 
        // In this case, the user should register a new Windows Hello user.
    
        if (string.IsNullOrEmpty(username))
        {
            return false;
        }
    
        if (!string.Equals(username, "sampleUsername"))
        {
            return false;
        }
    
        return true;
    }
    
  • La prochaine chose que vous devez faire est de gérer une demande de connexion de l’utilisateur. Dans Login.xaml.cs, créez une variable privée qui contiendra la connexion du compte actuel. Ajoutez ensuite une nouvelle méthode nommée SignInWindowsHelloAsync. Cela valide les informations d’identification du compte à l’aide de la méthode AccountHelper.ValidateAccountCredentials . Cette méthode retourne une valeur booléenne si le nom d’utilisateur entré est identique à la valeur de chaîne codée en dur que vous avez configurée à l’étape précédente. La valeur codée en dur pour cet exemple est « sampleUsername ».

    using WindowsHelloLogin.Models;
    using WindowsHelloLogin.Utils;
    using System.Diagnostics;
    using System.Threading.Tasks;
    
    namespace WindowsHelloLogin.Views
    {
        public sealed partial class Login : Page
        {
            private Account _account;
    
            public Login()
            {
                this.InitializeComponent();
            }
    
            protected override async void OnNavigatedTo(NavigationEventArgs e)
            {
                // Check if Windows Hello is set up and available on this machine
                if (await WindowsHelloHelper.WindowsHelloAvailableCheckAsync())
                {
                }
                else
                {
                    // Windows Hello is not set up, so inform the user
                    WindowsHelloStatus.Background = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 50, 170, 207));
                    WindowsHelloStatusText.Text = "Windows Hello is not set up!\nPlease go to Windows Settings and set up a PIN to use it.";
                    LoginButton.IsEnabled = false;
                }
            }
    
            private async void LoginButton_Click(object sender, RoutedEventArgs e)
            {
                ErrorMessage.Text = "";
                await SignInWindowsHelloAsync();
            }
    
            private void RegisterButtonTextBlock_OnPointerPressed(object sender, PointerRoutedEventArgs e)
            {
                ErrorMessage.Text = "";
            }
    
            private async Task SignInWindowsHelloAsync()
            {
                if (AccountHelper.ValidateAccountCredentials(UsernameTextBox.Text))
                {
                    // Create and add a new local account
                    _account = AccountHelper.AddAccount(UsernameTextBox.Text);
                    Debug.WriteLine("Successfully signed in with traditional credentials and created local account instance!");
    
                    //if (await WindowsHelloHelper.CreateWindowsHelloKeyAsync(UsernameTextBox.Text))
                    //{
                    //    Debug.WriteLine("Successfully signed in with Windows Hello!");
                    //}
                }
                else
                {
                    ErrorMessage.Text = "Invalid Credentials";
                }
            }
        }
    }
    
  • Vous avez peut-être remarqué le code commenté qui référençant une méthode dans WindowsHelloHelper. Dans WindowsHelloHelper.cs, ajoutez une nouvelle méthode nommée CreateWindowsHelloKeyAsync. Cette méthode utilise l’API Windows Hello dans KeyCredentialManager. L’appel de RequestCreateAsync crée une clé Windows Hello spécifique à l’id de compte et à l’ordinateur local. Notez les commentaires dans l’instruction switch si vous souhaitez implémenter cela dans un scénario réel.

    /// <summary>
    /// Creates a Windows Hello key on the machine using the account ID provided.
    /// </summary>
    /// <param name="accountId">The account ID associated with the account that we are enrolling into Windows Hello</param>
    /// <returns>Boolean indicating if creating the Windows Hello key succeeded</returns>
    public static async Task<bool> CreateWindowsHelloKeyAsync(string accountId)
    {
        KeyCredentialRetrievalResult keyCreationResult = await KeyCredentialManager.RequestCreateAsync(accountId, KeyCredentialCreationOption.ReplaceExisting);
    
        switch (keyCreationResult.Status)
        {
            case KeyCredentialStatus.Success:
                Debug.WriteLine("Successfully created key");
    
                // In the real world, authentication would take place on a server.
                // So, every time a user migrates or creates a new Windows Hello
                // account, details should be pushed to the server.
                // The details that would be pushed to the server include:
                // The public key, keyAttestation (if available), 
                // certificate chain for attestation endorsement key (if available),  
                // status code of key attestation result: keyAttestationIncluded or 
                // keyAttestationCanBeRetrievedLater and keyAttestationRetryType.
                // As this sample has no concept of a server, it will be skipped for now.
                // For information on how to do this, refer to the second sample.
    
                // For this sample, just return true
                return true;
            case KeyCredentialStatus.UserCanceled:
                Debug.WriteLine("User cancelled sign-in process.");
                break;
            case KeyCredentialStatus.NotFound:
                // User needs to set up Windows Hello
                Debug.WriteLine("Windows Hello is not set up!\nPlease go to Windows Settings and set up a PIN to use it.");
                break;
            default:
                break;
        }
    
        return false;
    }
    
  • Maintenant que vous avez créé la méthode CreateWindowsHelloKeyAsync , revenez au fichier Login.xaml.cs et supprimez les marques de commentaire du code à l’intérieur de la méthode SignInWindowsHelloAsync.

    private async void SignInWindowsHelloAsync()
    {
        if (AccountHelper.ValidateAccountCredentials(UsernameTextBox.Text))
        {
            //Create and add a new local account
            _account = AccountHelper.AddAccount(UsernameTextBox.Text);
            Debug.WriteLine("Successfully signed in with traditional credentials and created local account instance!");
    
            if (await WindowsHelloHelper.CreateWindowsHelloKeyAsync(UsernameTextBox.Text))
            {
                Debug.WriteLine("Successfully signed in with Windows Hello!");
            }
        }
        else
        {
            ErrorMessage.Text = "Invalid Credentials";
        }
    }
    
  • Générez et exécutez l’application. Vous êtes redirigé vers la page Connexion. Entrez le nom d’utilisateur « sampleUsername », puis cliquez sur Connexion. Vous serez invité à entrer votre code confidentiel avec une invite Windows Hello. Lorsque vous entrez correctement votre code pin, la méthode CreateWindowsHelloKeyAsync peut créer une clé Windows Hello. Surveillez les fenêtres de sortie pour voir si les messages indiquant la réussite sont affichés.

    Capture d’écran de l’invite de broche de connexion Windows Hello

Exercice 2 : Pages de sélection d’utilisateur et d’accueil

Dans cet exercice, vous allez continuer à partir de l’exercice précédent. Lorsqu’un utilisateur se connecte correctement, il doit être dirigé vers une page d’accueil où il peut se déconnecter ou supprimer son compte. Lorsque Windows Hello crée une clé pour chaque ordinateur, un écran de sélection d’utilisateur peut être créé, qui affiche tous les utilisateurs connectés sur cet ordinateur. Un utilisateur peut ensuite sélectionner l’un de ces comptes et accéder directement à l’écran d’accueil sans avoir à entrer à nouveau un mot de passe, car il a déjà été authentifié pour accéder à l’ordinateur.

  • Dans le dossier Vues , ajoutez une nouvelle page vide nommée « Welcome.xaml ». Ajoutez le code XAML suivant pour terminer l’interface utilisateur de la page. Cela affiche un titre, le nom d’utilisateur connecté et deux boutons. L’un des boutons revient à une liste d’utilisateurs (que vous créerez ultérieurement), et l’autre bouton gère l’oubli de cet utilisateur.

    <Grid>
      <StackPanel>
        <TextBlock x:Name="Title" Text="Welcome" FontSize="40" TextAlignment="Center"/>
        <TextBlock x:Name="UserNameText" FontSize="28" TextAlignment="Center"/>
    
        <Button x:Name="BackToUserListButton" Content="Back to User List" Click="Button_Restart_Click"
                HorizontalAlignment="Center" Margin="0,20" Foreground="White" Background="DodgerBlue"/>
    
        <Button x:Name="ForgetButton" Content="Forget Me" Click="Button_Forget_User_Click"
                Foreground="White"
                Background="Gray"
                HorizontalAlignment="Center"/>
      </StackPanel>
    </Grid>
    
  • Dans le Welcome.xaml.cs fichier code-behind, ajoutez une nouvelle variable privée qui contiendra le compte connecté. Vous devez implémenter une méthode pour remplacer l’événement OnNavigateTo , ce qui stocke le compte transmis à la page d’accueil . Vous devez également implémenter l’événement Click pour les deux boutons définis dans le code XAML. Vous devez ajouter des instructions using pour les espaces de noms et WindowsHelloLogin.Utils les WindowsHelloLogin.Models espaces de noms.

    using WindowsHelloLogin.Models;
    using WindowsHelloLogin.Utils;
    using System.Diagnostics;
    
    namespace WindowsHelloLogin.Views
    {
        public sealed partial class Welcome : Page
        {
            private Account _activeAccount;
    
            public Welcome()
            {
                InitializeComponent();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                _activeAccount = (Account)e.Parameter;
                if (_activeAccount != null)
                {
                    UserNameText.Text = _activeAccount.Username;
                }
            }
    
            private void Button_Restart_Click(object sender, RoutedEventArgs e)
            {
            }
    
            private void Button_Forget_User_Click(object sender, RoutedEventArgs e)
            {
                // Remove the account from Windows Hello
                // WindowsHelloHelper.RemoveWindowsHelloAccountAsync(_activeAccount);
    
                // Remove it from the local accounts list and re-save the updated list
                AccountHelper.RemoveAccount(_activeAccount);
    
                Debug.WriteLine($"User {_activeAccount.Username} deleted.");
            }
        }
    }
    
  • Vous avez peut-être remarqué un commentaire de ligne sorti dans le Button_Forget_User_Click gestionnaire d’événements. Le compte est supprimé de votre liste locale, mais il n’existe actuellement aucun moyen d’être supprimé de Windows Hello. Vous devez implémenter une nouvelle méthode dans WindowsHelloHelper.cs qui gère la suppression d’un utilisateur Windows Hello. Cette méthode utilise d’autres API Windows Hello pour ouvrir et supprimer le compte. Dans le monde réel, lorsque vous supprimez un compte, le serveur ou la base de données doit être averti afin que la base de données utilisateur reste valide. Vous aurez besoin d’une instruction using référençant l’espace WindowsHelloLogin.Models de noms.

    using WindowsHelloLogin.Models;
    
    /// <summary>
    /// Function to be called when user requests deleting their account.
    /// Checks the KeyCredentialManager to see if there is a Windows Hello
    /// account for the current user.
    /// It then deletes the local key associated with the account.
    /// </summary>
    public static async void RemoveWindowsHelloAccountAsync(Account account)
    {
        // Open the account with Windows Hello
        KeyCredentialRetrievalResult keyOpenResult = await KeyCredentialManager.OpenAsync(account.Username);
    
        if (keyOpenResult.Status == KeyCredentialStatus.Success)
        {
            // In the real world you would send key information to server to unregister
            //for example, RemoveWindowsHelloAccountOnServer(account);
        }
    
        // Then delete the account from the machine's list of Windows Hello accounts
        await KeyCredentialManager.DeleteAsync(account.Username);
    }
    
  • De retour dans Welcome.xaml.cs, supprimez les marques de commentaire de la ligne qui appelle RemoveWindowsHelloAccountAsync.

    private void Button_Forget_User_Click(object sender, RoutedEventArgs e)
    {
        // Remove it from Windows Hello
        WindowsHelloHelper.RemoveWindowsHelloAccountAsync(_activeAccount);
    
        // Remove it from the local accounts list and re-save the updated list
        AccountHelper.RemoveAccount(_activeAccount);
    
        Debug.WriteLine($"User {_activeAccount.Username} deleted.");
    }
    
  • Dans la méthode SignInWindowsHelloAsync (dans Login.xaml.cs), une fois que CreateWindowsHelloKeyAsync réussit, il doit accéder à la page d’accueil et passer le compte.

    private async void SignInWindowsHelloAsync()
    {
        if (AccountHelper.ValidateAccountCredentials(UsernameTextBox.Text))
        {
            // Create and add a new local account
            _account = AccountHelper.AddAccount(UsernameTextBox.Text);
            Debug.WriteLine("Successfully signed in with traditional credentials and created local account instance!");
    
            if (await WindowsHelloHelper.CreateWindowsHelloKeyAsync(UsernameTextBox.Text))
            {
                Debug.WriteLine("Successfully signed in with Windows Hello!");
                Frame.Navigate(typeof(Welcome), _account);
            }
        }
        else
        {
            ErrorMessage.Text = "Invalid Credentials";
        }
    }
    
  • Générez et exécutez l’application. Connectez-vous avec « sampleUsername », puis cliquez sur Connexion. Entrez votre code confidentiel et, si vous avez réussi, vous devez accéder à l’écran d’accueil. Essayez de cliquer sur Oublier l’utilisateur et de surveiller la fenêtre Sortie de Visual Studio pour voir si l’utilisateur a été supprimé. Notez que lorsque l’utilisateur est supprimé, vous restez sur la page d’accueil . Vous devez créer une page de sélection d’utilisateur vers laquelle l’application peut naviguer.

    Capture d’écran de l’écran d’accueil Windows Hello

  • Dans le dossier Views , créez une page vide nommée « UserSelection.xaml » et ajoutez le code XAML suivant pour définir l’interface utilisateur. Cette page contient un ListView qui affiche tous les utilisateurs de la liste des comptes locaux, ainsi qu’une Button page de connexion pour permettre à l’utilisateur d’ajouter un autre compte.

    <Grid>
      <StackPanel>
        <TextBlock x:Name="Title" Text="Select a User" FontSize="36" Margin="4" TextAlignment="Center" HorizontalAlignment="Center"/>
    
        <ListView x:Name="UserListView" Margin="4" MaxHeight="200" MinWidth="250" Width="250" HorizontalAlignment="Center">
          <ListView.ItemTemplate>
            <DataTemplate>
              <Grid Background="DodgerBlue" Height="50" Width="250" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <TextBlock Text="{Binding Username}" HorizontalAlignment="Center" TextAlignment="Center" VerticalAlignment="Center" Foreground="White"/>
              </Grid>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
    
        <Button x:Name="AddUserButton" Content="+" FontSize="36" Width="60" Click="AddUserButton_Click" HorizontalAlignment="Center"/>
      </StackPanel>
    </Grid>
    
  • Dans UserSelection.xaml.cs, implémentez la Loaded méthode qui accède à la page Connexion s’il n’existe aucun compte dans la liste locale. Implémentez également l’événement SelectionChanged pour l’événement ListView et un Click événement pour le Button.

    using System.Diagnostics;
    using WindowsHelloLogin.Models;
    using WindowsHelloLogin.Utils;
    
    namespace WindowsHelloLogin.Views
    {
        public sealed partial class UserSelection : Page
        {
            public UserSelection()
            {
                InitializeComponent();
                Loaded += UserSelection_Loaded;
            }
    
            private void UserSelection_Loaded(object sender, RoutedEventArgs e)
            {
                if (AccountHelper.AccountList.Count == 0)
                {
                    // If there are no accounts, navigate to the Login page
                    Frame.Navigate(typeof(Login));
                }
    
    
                UserListView.ItemsSource = AccountHelper.AccountList;
                UserListView.SelectionChanged += UserSelectionChanged;
            }
    
            /// <summary>
            /// Function called when an account is selected in the list of accounts
            /// Navigates to the Login page and passes the chosen account
            /// </summary>
            private void UserSelectionChanged(object sender, RoutedEventArgs e)
            {
                if (((ListView)sender).SelectedValue != null)
                {
                    Account account = (Account)((ListView)sender).SelectedValue;
                    if (account != null)
                    {
                        Debug.WriteLine($"Account {account.Username} selected!");
                    }
                    Frame.Navigate(typeof(Login), account);
                }
            }
    
            /// <summary>
            /// Function called when the "+" button is clicked to add a new user.
            /// Navigates to the Login page with nothing filled out
            /// </summary>
            private void AddUserButton_Click(object sender, RoutedEventArgs e)
            {
                Frame.Navigate(typeof(Login));
            }
        }
    }
    
  • Il existe quelques emplacements dans l’application où vous souhaitez accéder à la page UserSelection . Dans MainPage.xaml.cs, vous devez accéder à la page UserSelection au lieu de la page Connexion . Pendant que vous êtes dans l’événement chargé dans MainPage, vous devez charger la liste des comptes afin que la page UserSelection puisse vérifier s’il existe des comptes. Cela nécessite la modification de la Loaded méthode pour qu’elle soit asynchrone et l’ajout d’une instruction using pour l’espace WindowsHelloLogin.Utils de noms.

    using WindowsHelloLogin.Utils;
    
    private async void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        // Load the local account list before navigating to the UserSelection page
        await AccountHelper.LoadAccountListAsync();
        Frame.Navigate(typeof(UserSelection));
    }
    
  • Ensuite, l’application doit accéder à la page UserSelection à partir de la page d’accueil . Dans les deux Click événements, vous devez revenir à la page UserSelection .

    private void Button_Restart_Click(object sender, RoutedEventArgs e)
    {
        Frame.Navigate(typeof(UserSelection));
    }
    
    private void Button_Forget_User_Click(object sender, RoutedEventArgs e)
    {
        // Remove it from Windows Hello
        WindowsHelloHelper.RemoveWindowsHelloAccountAsync(_activeAccount);
    
        // Remove it from the local accounts list and re-save the updated list
        AccountHelper.RemoveAccount(_activeAccount);
    
        Debug.WriteLine($"User {_activeAccount.Username} deleted.");
    
        // Navigate back to UserSelection page.
        Frame.Navigate(typeof(UserSelection));
    }
    
  • Dans la page De connexion , vous avez besoin de code pour vous connecter au compte sélectionné dans la liste de la page UserSelection . Dans l’événement OnNavigatedTo , stockez le compte passé pendant la navigation. Commencez par ajouter une nouvelle variable privée qui identifie si le compte est un compte existant. Gérez ensuite l’événement OnNavigatedTo .

    namespace WindowsHelloLogin.Views
    {
        public sealed partial class Login : Page
        {
            private Account _account;
            private bool _isExistingAccount;
    
            public Login()
            {
                InitializeComponent();
            }
    
            /// <summary>
            /// Function called when this frame is navigated to.
            /// Checks to see if Windows Hello is available and if an account was passed in.
            /// If an account was passed in set the "_isExistingAccount" flag to true and set the _account.
            /// </summary>
            protected override async void OnNavigatedTo(NavigationEventArgs e)
            {
                // Check Windows Hello is set up and available on this machine
                if (await WindowsHelloHelper.WindowsHelloAvailableCheckAsync())
                {
                    if (e.Parameter != null)
                    {
                        _isExistingAccount = true;
                        // Set the account to the existing account being passed in
                        _account = (Account)e.Parameter;
                        UsernameTextBox.Text = _account.Username;
                        await SignInWindowsHelloAsync();
                    }
                }
                else
                {
                    // Windows Hello is not set up, so inform the user
                    WindowsHelloStatus.Background = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 50, 170, 207));
                    WindowsHelloStatusText.Text = $"Windows Hello is not set up!{Environment.NewLine}Please go to Windows Settings and set up a PIN to use it.";
                    LoginButton.IsEnabled = false;
                }
            }
        }
    }
    
  • La méthode SignInWindowsHelloAsync doit être mise à jour pour se connecter au compte sélectionné. WindowsHelloHelper aura besoin d’une autre méthode pour ouvrir le compte avec Windows Hello, car le compte dispose déjà d’une clé de compte créée pour celle-ci. Implémentez la nouvelle méthode dans WindowsHelloHelper.cs pour vous connecter à un utilisateur existant avec Windows Hello. Pour plus d’informations sur chaque partie du code, lisez les commentaires du code.

    /// <summary>
    /// Attempts to sign a message using the account key on the system for the accountId passed.
    /// </summary>
    /// <returns>Boolean representing if creating the Windows Hello authentication message succeeded</returns>
    public static async Task<bool> GetWindowsHelloAuthenticationMessageAsync(Account account)
    {
        KeyCredentialRetrievalResult openKeyResult = await KeyCredentialManager.OpenAsync(account.Username);
        // Calling OpenAsync will allow the user access to what is available in the app and will not require user credentials again.
        // If you wanted to force the user to sign in again you can use the following:
        // var consentResult = await Windows.Security.Credentials.UI.UserConsentVerifier.RequestVerificationAsync(account.Username);
        // This will ask for the either the password of the currently signed in Microsoft Account or the PIN used for Windows Hello.
    
        if (openKeyResult.Status == KeyCredentialStatus.Success)
        {
            // If OpenAsync has succeeded, the next thing to think about is whether the client application requires access to backend services.
            // If it does here you would request a challenge from the server. The client would sign this challenge and the server
            // would check the signed challenge. If it is correct, it would allow the user access to the backend.
            // You would likely make a new method called RequestSignAsync to handle all this.
            // For example, RequestSignAsync(openKeyResult);
            // Refer to the second Windows Hello sample for information on how to do this.
    
            // For this sample, there is not concept of a server implemented so just return true.
            return true;
        }
        else if (openKeyResult.Status == KeyCredentialStatus.NotFound)
        {
            // If the account is not found at this stage. It could be one of two errors. 
            // 1. Windows Hello has been disabled
            // 2. Windows Hello has been disabled and re-enabled cause the Windows Hello Key to change.
            // Calling CreateWindowsHelloKeyAsync and passing through the account will attempt to replace the existing Windows Hello Key for that account.
            // If the error really is that Windows Hello is disabled then the CreateWindowsHelloKeyAsync method will output that error.
            if (await CreateWindowsHelloKeyAsync(account.Username))
            {
                // If the Hello Key was again successfully created, Windows Hello has just been reset.
                // Now that the Hello Key has been reset for the account retry sign in.
                return await GetWindowsHelloAuthenticationMessageAsync(account);
            }
        }
    
        // Can't use Windows Hello right now, try again later
        return false;
    }
    
  • Mettez à jour la méthode SignInWindowsHelloAsync dans Login.xaml.cs pour gérer le compte existant. Cette méthode utilise la nouvelle méthode dans le WindowsHelloHelper.cs. Si le compte est correctement connecté et que l’utilisateur accède à la page d’accueil .

    private async Task SignInWindowsHelloAsync()
    {
        if (_isExistingAccount)
        {
            if (await WindowsHelloHelper.GetWindowsHelloAuthenticationMessageAsync(_account))
            {
                Frame.Navigate(typeof(Welcome), _account);
            }
        }
        else if (AccountHelper.ValidateAccountCredentials(UsernameTextBox.Text))
        {
            //Create and add a new local account
            _account = AccountHelper.AddAccount(UsernameTextBox.Text);
            Debug.WriteLine("Successfully signed in with traditional credentials and created local account instance!");
    
            if (await WindowsHelloHelper.CreateWindowsHelloKeyAsync(UsernameTextBox.Text))
            {
                Debug.WriteLine("Successfully signed in with Windows Hello!");
                Frame.Navigate(typeof(Welcome), _account);
            }
        }
        else
        {
            ErrorMessage.Text = "Invalid Credentials";
        }
    }
    
  • Générez et exécutez l’application. Connectez-vous avec « sampleUsername ». Tapez votre code confidentiel et, si vous avez réussi, vous accédez à la page d’accueil . Cliquez sur revenir à la liste des utilisateurs. Vous devez maintenant voir un utilisateur dans la liste. Si vous cliquez sur ce bouton, WindowsHello vous permet de vous reconnecter sans avoir à entrer à nouveau des mots de passe, etc.

    Capture d’écran de la liste d’utilisateurs Sélectionner Windows Hello

Exercice 3 : Inscription d’un nouvel utilisateur Windows Hello

Dans cet exercice, vous créez une page qui peut créer un compte avec Windows Hello. Cela fonctionne de la même façon que la page de connexion . La page Connexion est implémentée pour un utilisateur existant qui migre pour utiliser Windows Hello. Une page WindowsHelloRegister crée l’inscription Windows Hello pour un nouvel utilisateur.

  • Dans le dossier Vues , créez une page vide nommée « WindowsHelloRegister.xaml ». Dans le code XAML, ajoutez ce qui suit pour configurer l’interface utilisateur. L’interface de cette page est similaire à la page Connexion .

    <Grid>
      <StackPanel>
        <TextBlock x:Name="Title" Text="Register New Windows Hello User" FontSize="24" Margin="4" TextAlignment="Center"/>
    
        <TextBlock x:Name="ErrorMessage" Text="" FontSize="20" Margin="4" Foreground="Red" TextAlignment="Center"/>
    
        <TextBlock Text="Enter your new username below" Margin="0,0,0,20"
                   TextWrapping="Wrap" Width="300"
                   TextAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
    
        <TextBox x:Name="UsernameTextBox" Margin="4" Width="250"/>
    
        <Button x:Name="RegisterButton" Content="Register" Background="DodgerBlue" Foreground="White"
                Click="RegisterButton_Click_Async" Width="80" HorizontalAlignment="Center" Margin="0,20"/>
    
        <Border x:Name="WindowsHelloStatus" Background="#22B14C" Margin="4" Height="100">
          <TextBlock x:Name="WindowsHelloStatusText" Text="Windows Hello is ready to use!" FontSize="20"
                     Margin="4" TextAlignment="Center" VerticalAlignment="Center"/>
        </Border>
      </StackPanel>
    </Grid>
    
  • Dans le WindowsHelloRegister.xaml.cs fichier code-behind, implémentez une variable privée Account et un Click événement pour le bouton Inscrire. Cela ajoute un nouveau compte local et crée une clé Windows Hello.

    using Microsoft.UI.Xaml.Controls;
    using Microsoft.UI.Xaml;
    using WindowsHelloLogin.Models;
    using WindowsHelloLogin.Utils;
    
    namespace WindowsHelloLogin.Views
    {
        public sealed partial class WindowsHelloRegister : Page
        {
            private Account _account;
    
            public WindowsHelloRegister()
            {
                InitializeComponent();
            }
    
            private async void RegisterButton_Click_Async(object sender, RoutedEventArgs e)
            {
                ErrorMessage.Text = "";
    
                // In the real world, you would validate the entered credentials and information before 
                // allowing a user to register a new account. 
                // For this sample, we'll skip that step and just register an account if the username is not null.
    
                if (!string.IsNullOrEmpty(UsernameTextBox.Text))
                {
                    // Register a new account
                    _account = AccountHelper.AddAccount(UsernameTextBox.Text);
                    // Register new account with Windows Hello
                    await WindowsHelloHelper.CreateWindowsHelloKeyAsync(_account.Username);
                    // Navigate to the Welcome page. 
                    Frame.Navigate(typeof(Welcome), _account);
                }
                else
                {
                    ErrorMessage.Text = "Please enter a username";
                }
            }
        }
    }
    
  • Vous devez accéder à cette page à partir de la page de connexion lorsque l’inscription est activée.

    private void RegisterButtonTextBlock_OnPointerPressed(object sender, PointerRoutedEventArgs e)
    {
        ErrorMessage.Text = "";
        Frame.Navigate(typeof(WindowsHelloRegister));
    }
    
  • Générez et exécutez l’application. Essayez d’inscrire un nouvel utilisateur. Revenez ensuite à la liste des utilisateurs et vérifiez que vous pouvez sélectionner cet utilisateur et cette connexion.

    Capture d’écran de la page Inscription d’un nouvel utilisateur Windows Hello

Dans ce labo, vous avez appris les compétences essentielles nécessaires pour utiliser la nouvelle API Windows Hello pour authentifier les utilisateurs existants et créer des comptes pour les nouveaux utilisateurs. Avec cette nouvelle connaissance, vous pouvez commencer à supprimer la nécessité pour les utilisateurs de mémoriser les mots de passe de votre application, tout en restant confiant que vos applications restent protégées par l’authentification utilisateur. Windows utilise la nouvelle technologie d’authentification de Windows Hello pour prendre en charge ses options de connexion biométriques.