Partager via


Créer un service de connexion Windows Hello

Il s’agit de la partie 2 d’une procédure pas à pas complète sur l’utilisation de Windows Hello comme alternative aux systèmes d’authentification de nom d’utilisateur et de mot de passe traditionnels dans les applications Windows 10 et Windows 11 plateforme Windows universelle (UWP). Cet article récupère l’emplacement où la partie 1, l’application de connexion Windows Hello, est désactivée et étend les fonctionnalités pour montrer comment intégrer Windows Hello à votre application existante.

Pour générer ce projet, vous aurez besoin d’une expérience avec C# et XAML. Vous devez également utiliser Visual Studio 2015 (Community Edition ou version ultérieure) sur un ordinateur Windows 10 ou Windows 11.

Exercice 1 : Logique côté serveur

Dans cet exercice, vous allez commencer par l’application Windows Hello créée dans le premier labo et la création d’un serveur fictif local et d’une base de données. Ce laboratoire pratique est conçu pour apprendre comment Windows Hello peut être intégré à un système existant. L’utilisation d’un serveur fictif et d’une base de données fictive est supprimée. Dans vos propres applications, vous devez remplacer les objets fictifs par les services et bases de données réels.

  • Pour commencer, ouvrez la solution PassportLogin à partir du premier laboratoire passport Hands On Lab.

  • Vous commencerez par implémenter le serveur fictif et la base de données fictive. Créez un dossier appelé « AuthService ». Dans l’Explorateur de solutions, cliquez avec le bouton droit sur la solution « PassportLogin (Windows universel) », puis sélectionnez Ajouter > un nouveau dossier.

  • Créez des classes UserAccount et PassportDevices qui agiront en tant que modèles pour que les données soient enregistrées dans la base de données fictif. UserAccount sera similaire au modèle utilisateur implémenté sur un serveur d’authentification traditionnel. Cliquez avec le bouton droit sur le dossier AuthService et ajoutez une nouvelle classe appelée « UserAccount.cs ».

    Dossier de création d’autorisation Windows Hello

    Classe de création d’autorisation Windows Hello

  • Modifiez la définition de classe pour qu’elle soit publique, puis ajoutez les propriétés publiques suivantes. Vous aurez besoin de la référence suivante.

    using System.ComponentModel.DataAnnotations;
    
    namespace PassportLogin.AuthService
    {
        public class UserAccount
        {
            [Key, Required]
            public Guid UserId { get; set; }
            [Required]
            public string Username { get; set; }
            public string Password { get; set; }
            // public List<PassportDevice> PassportDevices = new List<PassportDevice>();
        }
    }
    

    Vous avez peut-être remarqué la liste commentée de PassportDevices. Il s’agit d’une modification que vous devez apporter à un modèle utilisateur existant dans votre implémentation actuelle. La liste des PassportDevices contiendra un deviceID, la clé publique effectuée à partir de Windows Hello et une KeyCredentialAttestationResult. Pour ce labo, vous devez implémenter la cléAttestationResult, car elles sont fournies uniquement par Windows Hello sur les appareils qui ont une puce TPM (Modules de plateforme sécurisée). KeyCredentialAttestationResult est une combinaison de plusieurs propriétés et doit être fractionnée pour les enregistrer et les charger avec une base de données.

  • Créez une classe dans le dossier AuthService appelé « PassportDevice.cs ». Il s’agit du modèle des appareils Windows Hello, comme indiqué ci-dessus. Modifiez la définition de classe pour qu’elle soit publique et ajoutez les propriétés suivantes.

    namespace PassportLogin.AuthService
    {
        public class PassportDevice
        {
            // These are the new variables that will need to be added to the existing UserAccount in the Database
            // The DeviceName is used to support multiple devices for the one user.
            // This way the correct public key is easier to find as a new public key is made for each device.
            // The KeyAttestationResult is only used if the User device has a TPM (Trusted Platform Module) chip, 
            // in most cases it will not. So will be left out for this hands on lab.
            public Guid DeviceId { get; set; }
            public byte[] PublicKey { get; set; }
            // public KeyCredentialAttestationResult KeyAttestationResult { get; set; }
        }
    }
    
  • Revenez à UserAccount.cs et supprimez les marques de commentaire de la liste des appareils Windows Hello.

    using System.Collections.Generic;
    
    namespace PassportLogin.AuthService
    {
        public class UserAccount
        {
            [Key, Required]
            public Guid UserId { get; set; }
            [Required]
            public string Username { get; set; }
            public string Password { get; set; }
            public List<PassportDevice> PassportDevices = new List<PassportDevice>();
        }
    }
    
  • Avec le modèle pour UserAccount et PassportDevice créé, vous devez créer une autre classe dans AuthService qui servira de base de données fictif. Comme il s’agit d’une base de données fictif à partir de laquelle vous allez enregistrer et charger une liste de comptes d’utilisateur localement. Dans le monde réel, il s’agirait de votre implémentation de base de données. Créez une classe dans AuthService appelée « MockStore.cs ». Modifiez la définition de classe en public.

  • Comme le magasin fictif enregistre et charge une liste de comptes d’utilisateur localement, vous pouvez implémenter la logique pour enregistrer et charger cette liste à l’aide d’un xmlSerializer. Vous devez également mémoriser le nom de fichier et enregistrer l’emplacement. Dans MockStore.cs implémenter les éléments suivants :

    using System.IO;
    using System.Linq;
    using System.Xml.Serialization;
    using Windows.Storage;

    namespace PassportLogin.AuthService
    {
        public class MockStore
        {
            private const string USER_ACCOUNT_LIST_FILE_NAME = "userAccountsList.txt";
            // This cannot be a const because the LocalFolder is accessed at runtime
            private string _userAccountListPath = Path.Combine(
                ApplicationData.Current.LocalFolder.Path, USER_ACCOUNT_LIST_FILE_NAME);
            private List<UserAccount> _mockDatabaseUserAccountsList;
     
    #region Save and Load Helpers
            /// <summary>
            /// Create and save a useraccount list file. (Replacing the old one)
            /// </summary>
            private async void SaveAccountListAsync()
            {
                string accountsXml = SerializeAccountListToXml();
     
                if (File.Exists(_userAccountListPath))
                {
                    StorageFile accountsFile = await StorageFile.GetFileFromPathAsync(_userAccountListPath);
                    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>
            private async void LoadAccountListAsync()
            {
                if (File.Exists(_userAccountListPath))
                {
                    StorageFile accountsFile = await StorageFile.GetFileFromPathAsync(_userAccountListPath);
     
                    string accountsXml = await FileIO.ReadTextAsync(accountsFile);
                    DeserializeXmlToAccountList(accountsXml);
                }
     
                // If the UserAccountList does not contain the sampleUser Initialize the sample users
                // This is only needed as it in a Hand on Lab to demonstrate a user migrating
                // In the real world user accounts would just be in a database
                if (!_mockDatabaseUserAccountsList.Any(f => f.Username.Equals("sampleUsername")))
                {
                    //If the list is empty InitializeSampleAccounts and return the list
                    //InitializeSampleUserAccounts();
                }
            }
     
            /// <summary>
            /// Uses the local list of accounts and returns an XML formatted string representing the list
            /// </summary>
            /// <returns>XML formatted list of accounts</returns>
            private string SerializeAccountListToXml()
            {
                XmlSerializer xmlizer = new XmlSerializer(typeof(List<UserAccount>));
                StringWriter writer = new StringWriter();
                xmlizer.Serialize(writer, _mockDatabaseUserAccountsList);
                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>
            private List<UserAccount> DeserializeXmlToAccountList(string listAsXml)
            {
                XmlSerializer xmlizer = new XmlSerializer(typeof(List<UserAccount>));
                TextReader textreader = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(listAsXml)));
                return _mockDatabaseUserAccountsList = (xmlizer.Deserialize(textreader)) as List<UserAccount>;
            }
    #endregion
        }
    }
  • Dans la méthode de chargement, vous avez peut-être remarqué qu’une méthode InitializeSampleUserAccounts a été commentée. Vous devez créer cette méthode dans le MockStore.cs. Cette méthode remplit la liste des comptes d’utilisateur afin qu’une connexion puisse avoir lieu. Dans le monde réel, la base de données utilisateur serait déjà remplie. Dans cette étape, vous allez également créer un constructeur qui initialise la liste des utilisateurs et le chargement des appels.

    namespace PassportLogin.AuthService
    {
        public class MockStore
        {
            private const string USER_ACCOUNT_LIST_FILE_NAME = "userAccountsList.txt";
            // This cannot be a const because the LocalFolder is accessed at runtime
            private string _userAccountListPath = Path.Combine(
                ApplicationData.Current.LocalFolder.Path, USER_ACCOUNT_LIST_FILE_NAME);
            private List<UserAccount> _mockDatabaseUserAccountsList;
    
            public MockStore()
            {
                _mockDatabaseUserAccountsList = new List<UserAccount>();
                LoadAccountListAsync();
            }
    
            private void InitializeSampleUserAccounts()
            {
                // Create a sample Traditional User Account that only has a Username and Password
                // This will be used initially to demonstrate how to migrate to use Windows Hello
    
                UserAccount sampleUserAccount = new UserAccount()
                {
                    UserId = Guid.NewGuid(),
                    Username = "sampleUsername",
                    Password = "samplePassword",
                };
    
                // Add the sampleUserAccount to the _mockDatabase
                _mockDatabaseUserAccountsList.Add(sampleUserAccount);
                SaveAccountListAsync();
            }
        }
    }
    
  • Maintenant que la méthode InitalizeSampleUserAccounts existe uncomment l’appel de méthode dans la méthode LoadAccountListAsync.

    private async void LoadAccountListAsync()
    {
        if (File.Exists(_userAccountListPath))
        {
            StorageFile accountsFile = await StorageFile.GetFileFromPathAsync(_userAccountListPath);
    
            string accountsXml = await FileIO.ReadTextAsync(accountsFile);
            DeserializeXmlToAccountList(accountsXml);
        }
    
        // If the UserAccountList does not contain the sampleUser Initialize the sample users
        // This is only needed as it in a Hand on Lab to demonstrate a user migrating
        // In the real world user accounts would just be in a database
        if (!_mockDatabaseUserAccountsList.Any(f = > f.Username.Equals("sampleUsername")))
                {
            //If the list is empty InitializeSampleAccounts and return the list
            InitializeSampleUserAccounts();
        }
    }
    
  • La liste des comptes d’utilisateur dans le magasin fictif peut désormais être enregistrée et chargée. D’autres parties de l’application devront avoir accès à cette liste afin qu’il y ait des méthodes pour récupérer ces données. Sous la méthode InitializeSampleUserAccounts, ajoutez les méthodes get suivantes. Ils vous permettront d’obtenir un userid, un seul utilisateur, une liste d’utilisateurs pour un appareil Windows Hello spécifique et d’obtenir également la clé publique pour l’utilisateur sur un appareil spécifique.

    public Guid GetUserId(string username)
    {
        if (_mockDatabaseUserAccountsList.Any())
        {
            UserAccount account = _mockDatabaseUserAccountsList.FirstOrDefault(f => f.Username.Equals(username));
            if (account != null)
            {
                return account.UserId;
            }
        }
        return Guid.Empty;
    }
    
    public UserAccount GetUserAccount(Guid userId)
    {
        return _mockDatabaseUserAccountsList.FirstOrDefault(f => f.UserId.Equals(userId));
    }
    
    public List<UserAccount> GetUserAccountsForDevice(Guid deviceId)
    {
        List<UserAccount> usersForDevice = new List<UserAccount>();
    
        foreach (UserAccount account in _mockDatabaseUserAccountsList)
        {
            if (account.PassportDevices.Any(f => f.DeviceId.Equals(deviceId)))
            {
                usersForDevice.Add(account);
            }
        }
    
        return usersForDevice;
    }
    
    public byte[] GetPublicKey(Guid userId, Guid deviceId)
    {
        UserAccount account = _mockDatabaseUserAccountsList.FirstOrDefault(f => f.UserId.Equals(userId));
        if (account != null)
        {
            if (account.PassportDevices.Any())
            {
                return account.PassportDevices.FirstOrDefault(p => p.DeviceId.Equals(deviceId)).PublicKey;
            }
        }
        return null;
    }
    
  • Les méthodes suivantes à implémenter gèrent des opérations simples pour ajouter un compte, supprimer un compte et également supprimer l’appareil. Supprimer l’appareil est nécessaire, car Windows Hello est spécifique à l’appareil. Pour chaque appareil auquel vous vous connectez, une nouvelle paire de clés publique et privée sera créée par Windows Hello. Il est semblable à avoir un mot de passe différent pour chaque appareil que vous connectez, la seule chose est que vous n’avez pas besoin de mémoriser tous ces mots de passe que le serveur fait. Ajoutez les méthodes suivantes dans le MockStore.cs

    public UserAccount AddAccount(string username)
    {
        UserAccount newAccount = null;
        try
        {
            newAccount = new UserAccount()
            {
                UserId = Guid.NewGuid(),
                Username = username,
            };
    
            _mockDatabaseUserAccountsList.Add(newAccount);
            SaveAccountListAsync();
        }
        catch (Exception)
        {
            throw;
        }
        return newAccount;
    }
    
    public bool RemoveAccount(Guid userId)
    {
        UserAccount userAccount = GetUserAccount(userId);
        if (userAccount != null)
        {
            _mockDatabaseUserAccountsList.Remove(userAccount);
            SaveAccountListAsync();
            return true;
        }
        return false;
    }
    
    public bool RemoveDevice(Guid userId, Guid deviceId)
    {
        UserAccount userAccount = GetUserAccount(userId);
        PassportDevice deviceToRemove = null;
        if (userAccount != null)
        {
            foreach (PassportDevice device in userAccount.PassportDevices)
            {
                if (device.DeviceId.Equals(deviceId))
                {
                    deviceToRemove = device;
                    break;
                }
            }
        }
    
        if (deviceToRemove != null)
        {
            //Remove the PassportDevice
            userAccount.PassportDevices.Remove(deviceToRemove);
            SaveAccountListAsync();
        }
    
        return true;
    }
    
  • Dans la classe MockStore, ajoutez une méthode qui ajoute des informations associées à Windows Hello à un UserAccount existant. Cette méthode sera appelée PassportUpdateDetails et prendra des paramètres pour identifier l’utilisateur, ainsi que les détails de Windows Hello. KeyAttestationResult a été commenté lors de la création d’un PassportDevice, dans une application réelle dont vous avez besoin.

    using Windows.Security.Credentials;
    
    public void PassportUpdateDetails(Guid userId, Guid deviceId, byte[] publicKey, 
        KeyCredentialAttestationResult keyAttestationResult)
    {
        UserAccount existingUserAccount = GetUserAccount(userId);
        if (existingUserAccount != null)
        {
            if (!existingUserAccount.PassportDevices.Any(f => f.DeviceId.Equals(deviceId)))
            {
                existingUserAccount.PassportDevices.Add(new PassportDevice()
                {
                    DeviceId = deviceId,
                    PublicKey = publicKey,
                    // KeyAttestationResult = keyAttestationResult
                });
            }
        }
        SaveAccountListAsync();
    }
    
  • La classe MockStore est maintenant terminée, car elle représente la base de données qu’elle doit être considérée comme privée. Pour accéder à la classe MockStore, une classe AuthService est nécessaire pour manipuler les données de base de données. Dans le dossier AuthService, créez une classe appelée « AuthService.cs ». Modifiez la définition de classe en public et ajoutez un modèle d’instance singleton pour vous assurer qu’une seule instance n’est jamais créée.

    namespace PassportLogin.AuthService
    {
        public class AuthService
        {
            // Singleton instance of the AuthService
            // The AuthService is a mock of what a real world server and service implementation would be
            private static AuthService _instance;
            public static AuthService Instance
            {
                get
                {
                    if (null == _instance)
                    {
                        _instance = new AuthService();
                    }
                    return _instance;
                }
            }
    
            private AuthService()
            { }
        }
    }
    
  • La classe AuthService doit créer une instance de la classe MockStore et fournir l’accès aux propriétés de l’objet MockStore.

    namespace PassportLogin.AuthService
    {
        public class AuthService
        {
            //Singleton instance of the AuthService
            //The AuthService is a mock of what a real world server and database implementation would be
            private static AuthService _instance;
            public static AuthService Instance
            {
                get
                {
                    if (null == _instance)
                    {
                        _instance = new AuthService();
                    }
                    return _instance;
                }
            }
    
            private AuthService()
            { }
    
            private MockStore _mockStore = new MockStore();
    
            public Guid GetUserId(string username)
            {
                return _mockStore.GetUserId(username);
            }
    
            public UserAccount GetUserAccount(Guid userId)
            {
                return _mockStore.GetUserAccount(userId);
            }
    
            public List<UserAccount> GetUserAccountsForDevice(Guid deviceId)
            {
                return _mockStore.GetUserAccountsForDevice(deviceId);
            }
        }
    }
    
  • Vous avez besoin de méthodes dans la classe AuthService pour accéder aux méthodes d’ajout, de suppression et de mise à jour des méthodes de détails de passeport dans l’objet MockStore. À la fin du fichier de classe AuthService, ajoutez les méthodes suivantes.

    using Windows.Security.Credentials;
    
    public void Register(string username)
    {
        _mockStore.AddAccount(username);
    }
    
    public bool PassportRemoveUser(Guid userId)
    {
        return _mockStore.RemoveAccount(userId);
    }
    
    public bool PassportRemoveDevice(Guid userId, Guid deviceId)
    {
        return _mockStore.RemoveDevice(userId, deviceId);
    }
    
    public void PassportUpdateDetails(Guid userId, Guid deviceId, byte[] publicKey, 
        KeyCredentialAttestationResult keyAttestationResult)
    {
        _mockStore.PassportUpdateDetails(userId, deviceId, publicKey, keyAttestationResult);
    }
    
  • La classe AuthService doit fournir une méthode pour valider les informations d’identification. Cette méthode prend un nom d’utilisateur et un mot de passe et vérifie que le compte existe et que le mot de passe est valide. Un système existant aurait une méthode équivalente à celle-ci qui vérifie que l’utilisateur est autorisé. Ajoutez les validateCredentials suivants au fichier AuthService.cs.

    public bool ValidateCredentials(string username, string password)
    {
        if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
        {
            // This would be used for existing accounts migrating to use Passport
            Guid userId = GetUserId(username);
            if (userId != Guid.Empty)
            {
                UserAccount account = GetUserAccount(userId);
                if (account != null)
                {
                    if (string.Equals(password, account.Password))
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    
  • La classe AuthService a besoin d’une méthode de défi de requête qui renverra un défi au client pour valider l’utilisateur qui prétend être. Ensuite, une méthode est nécessaire dans la classe AuthService pour recevoir le défi signé du client. Pour ce labo pratique, la méthode de détermination de la façon dont vous déterminez si le défi signé a été terminé a été laissé incomplet. Chaque implémentation de Windows Hello dans un système d’authentification existant sera légèrement différente. La clé publique stockée sur le serveur doit correspondre au résultat retourné par le client au serveur. Ajoutez ces deux méthodes à AuthService.cs.

    using Windows.Security.Cryptography;
    using Windows.Storage.Streams;
    
    public IBuffer PassportRequestChallenge()
    {
        return CryptographicBuffer.ConvertStringToBinary("ServerChallenge", BinaryStringEncoding.Utf8);
    }
    
    public bool SendServerSignedChallenge(Guid userId, Guid deviceId, byte[] signedChallenge)
    {
        // Depending on your company polices and procedures this step will be different
        // It is at this point you will need to validate the signedChallenge that is sent back from the client.
        // Validation is used to ensure the correct user is trying to access this account. 
        // The validation process will use the signedChallenge and the stored PublicKey 
        // for the username and the specific device signin is called from.
        // Based on the validation result you will return a bool value to allow access to continue or to block the account.
    
        // For this sample validation will not happen as a best practice solution does not apply and will need to 
           // be configured for each company.
        // Simply just return true.
    
        // You could get the User's Public Key with something similar to the following:
        byte[] userPublicKey = _mockStore.GetPublicKey(userId, deviceId);
        return true;
    }
    

Exercice 2 : Logique côté client

Dans cet exercice, vous allez modifier les vues côté client et les classes d’assistance du premier labo pour utiliser la classe AuthService. Dans le monde réel, AuthService serait le serveur d’authentification et vous devez utiliser les API web pour envoyer et recevoir des données du serveur. Pour cette pratique, le client lab et le serveur sont tous locaux pour garder les choses simples. L’objectif est d’apprendre à utiliser les API Windows Hello.

  • Dans le MainPage.xaml.cs vous pouvez supprimer l’appel de méthode AccountHelper.LoadAccountListAsync dans la méthode chargée, car la classe AuthService crée une instance du MockStore qui charge la liste des comptes. La méthode chargée doit maintenant ressembler à ce qui suit. Notez que la définition de méthode asynchrone est supprimée, car rien n’attend.

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        Frame.Navigate(typeof(UserSelection));
    }
    
  • Mettez à jour l’interface de page de connexion pour exiger l’entrée d’un passeport. Cette pratique montre comment un système existant peut être migré pour utiliser Windows Hello et les comptes existants aura un nom d’utilisateur et un mot de passe. Mettez également à jour l’explication en bas du code XAML pour inclure le mot de passe par défaut. Mettre à jour le code XAML suivant dans Login.xaml

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
      <StackPanel Orientation="Vertical">
        <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 credentials below" Margin="0,0,0,20"
                   TextWrapping="Wrap" Width="300"
                   TextAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
    
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
          <!-- Username Input -->
          <TextBlock x:Name="UserNameTextBlock" Text="Username: "
                 FontSize="20" Margin="4" Width="100"/>
          <TextBox x:Name="UsernameTextBox" PlaceholderText="sampleUsername" Width="200" Margin="4"/>
        </StackPanel>
    
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
          <!-- Password Input -->
          <TextBlock x:Name="PasswordTextBlock" Text="Password: "
                 FontSize="20" Margin="4" Width="100"/>
          <PasswordBox x:Name="PasswordBox" PlaceholderText="samplePassword" Width="200" Margin="4"/>
        </StackPanel>
    
        <Button x:Name="PassportSignInButton" Content="Login" Background="DodgerBlue" Foreground="White"
            Click="PassportSignInButton_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="PassportStatus" Background="#22B14C"
                   Margin="0,20" Height="100">
          <TextBlock x:Name="PassportStatusText" Text="Windows Hello is ready to use!"
                 Margin="4" TextAlignment="Center" VerticalAlignment="Center" FontSize="20"/>
        </Border>
    
        <TextBlock x:Name="LoginExplaination" FontSize="24" TextAlignment="Center" TextWrapping="Wrap" 
            Text="Please Note: To demonstrate a login, validation will only occur using the default username 
            'sampleUsername' and default password 'samplePassword'"/>
    
      </StackPanel>
    </Grid>
    
  • Dans le code de classe Login derrière vous devez modifier la variable privée Account en haut de la classe pour qu’elle soit UserAccount. Modifiez l’événement OnNavigateTo pour convertir le type en un UserAccount. Mettez également à jour les appels à SignInPassport pour effectuer des appels asynchrones à SignInPassportAsync à la place. Vous aurez besoin de la référence suivante.

    using PassportLogin.AuthService;
    
    namespace PassportLogin.Views
    {
        public sealed partial class Login : Page
        {
            private UserAccount _account;
            private bool _isExistingAccount;
    
            public Login()
            {
                this.InitializeComponent();
            }
    
            protected override async void OnNavigatedTo(NavigationEventArgs e)
            {
                //Check Windows Hello is setup and available on this machine
                if (await MicrosoftPassportHelper.MicrosoftPassportAvailableCheckAsync())
                {
                    if (e.Parameter != null)
                    {
                        _isExistingAccount = true;
                        //Set the account to the existing account being passed in
                        _account = (UserAccount)e.Parameter;
                        UsernameTextBox.Text = _account.Username;
                        await SignInPassportAsync();
                    }
                }
            }
    
            private async void PassportSignInButton_Click(object sender, RoutedEventArgs e)
            {
                ErrorMessage.Text = "";
                await SignInPassportAsync();
            }
        }
    }
    
  • Comme la page Connexion utilise un objet UserAccount au lieu de l’objet Account précédent, l’MicrosoftPassportHelper.cs doit être mis à jour pour utiliser un UserAccount comme paramètre pour certaines méthodes. Vous devez modifier les paramètres suivants pour les méthodes CreatePassportKeyAsync, RemovePassportAccountAsync et GetPassportAuthenticationMessageAsync. Comme la classe UserAccount a un GUID pour un UserId, vous commencerez à utiliser l’ID dans d’autres emplacements pour être plus spécifique.

    public static async Task<bool> CreatePassportKeyAsync(Guid userId, string username)
    {
        KeyCredentialRetrievalResult keyCreationResult = await KeyCredentialManager.RequestCreateAsync(username, KeyCredentialCreationOption.ReplaceExisting);
    
        return true;
    }
    
    public static async void RemovePassportAccountAsync(UserAccount account)
    {
    
    }
    public static async Task<bool> GetPassportAuthenticationMessageAsync(UserAccount 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 CreatePassportKey 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 CreatePassportKey method will output that error.
            if (await CreatePassportKeyAsync(account.UserId, account.Username))
            {
                //If the Passport Key was again successfully created, Windows Hello has just been reset.
                //Now that the Passport Key has been reset for the _account retry sign in.
                return await GetPassportAuthenticationMessageAsync(account);
            }
        }
    
        // Can't use Passport right now, try again later
        return false;
    }
    
  • La méthode SignInPassportAsync dans Login.xaml.cs fichier doit être mise à jour pour utiliser AuthService au lieu de AccountHelper. La validation des informations d’identification se produit via AuthService. Pour cette pratique, le seul compte configuré est « sampleUsername ». Ce compte est créé dans la méthode InitializeSampleUserAccounts dans MockStore.cs. Mettez à jour la méthode SignInPassportAsync dans Login.xaml.cs maintenant pour refléter l’extrait de code ci-dessous.

    private async void SignInPassportAsync()
    {
        if (_isExistingAccount)
        {
            if (await MicrosoftPassportHelper.GetPassportAuthenticationMessageAsync(_account))
            {
                Frame.Navigate(typeof(Welcome), _account);
            }
        }
        else if (AuthService.AuthService.Instance.ValidateCredentials(UsernameTextBox.Text, PasswordBox.Password))
        {
            Guid userId = AuthService.AuthService.Instance.GetUserId(UsernameTextBox.Text);
    
            if (userId != Guid.Empty)
            {
                //Now that the account exists on server try and create the necessary passport details and add them to the account
                bool isSuccessful = await MicrosoftPassportHelper.CreatePassportKeyAsync(userId, UsernameTextBox.Text);
                if (isSuccessful)
                {
                    Debug.WriteLine("Successfully signed in with Windows Hello!");
                    //Navigate to the Welcome Screen. 
                    _account = AuthService.AuthService.Instance.GetUserAccount(userId);
                    Frame.Navigate(typeof(Welcome), _account);
                }
                else
                {
                    //The passport account creation failed.
                    //Remove the account from the server as passport details were not configured
                    AuthService.AuthService.Instance.PassportRemoveUser(userId);
    
                    ErrorMessage.Text = "Account Creation Failed";
                }
            }
        }
        else
        {
            ErrorMessage.Text = "Invalid Credentials";
        }
    }
    
  • Comme Windows Hello crée une paire de clés publique et privée différente pour chaque compte sur chaque appareil, la page d’accueil doit afficher une liste d’appareils inscrits pour le compte connecté et permettre à chacun d’être oublié. Dans Welcome.xaml, ajoutez le code XAML suivant sous l’objet ForgetButton. Cela implémente un bouton oublier l’appareil, une zone de texte d’erreur et une liste pour afficher tous les appareils.

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
      <StackPanel Orientation="Vertical">
        <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"/>
    
        <Button x:Name="ForgetDeviceButton" Content="Forget Device" Click="Button_Forget_Device_Click"
               Foreground="White"
               Background="Gray"
               Margin="0,40,0,20"
               HorizontalAlignment="Center"/>
    
        <TextBlock x:Name="ForgetDeviceErrorTextBlock" Text="Select a device first"
                  TextWrapping="Wrap" Width="300" Foreground="Red"
                  TextAlignment="Center" VerticalAlignment="Center" FontSize="16" Visibility="Collapsed"/>
    
        <ListView x:Name="UserListView" MaxHeight="500" MinWidth="350" Width="350" HorizontalAlignment="Center">
          <ListView.ItemTemplate>
            <DataTemplate>
              <Grid Background="Gray" Height="50" Width="350" HorizontalAlignment="Center" VerticalAlignment="Stretch" >
                <TextBlock Text="{Binding DeviceId}" HorizontalAlignment="Center" TextAlignment="Center" VerticalAlignment="Center"
                          Foreground="White"/>
              </Grid>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
      </StackPanel>
    </Grid>
    
  • Dans le fichier Welcome.xaml.cs, vous devez modifier la variable compte privé en haut de la classe pour qu’elle soit une variable UserAccount privée. Mettez ensuite à jour la méthode OnNavigatedTo pour utiliser AuthService et récupérer des informations pour le compte actuel. Lorsque vous disposez des informations de compte, vous pouvez définir ItemsSource de la liste pour afficher les appareils. Vous devez ajouter une référence à l’espace de noms AuthService.

    using PassportLogin.AuthService;
    
    namespace PassportLogin.Views
    {
        public sealed partial class Welcome : Page
        {
            private UserAccount _activeAccount;
    
            public Welcome()
            {
                InitializeComponent();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                _activeAccount = (UserAccount)e.Parameter;
                if (_activeAccount != null)
                {
                    UserAccount account = AuthService.AuthService.Instance.GetUserAccount(_activeAccount.UserId);
                    if (account != null)
                    {
                        UserListView.ItemsSource = account.PassportDevices;
                        UserNameText.Text = account.Username;
                    }
                }
            }
        }
    }
    
  • Comme vous utiliserez AuthService lors de la suppression d’un compte, vous pouvez supprimer la référence à AccountHelper dans la méthode Button_Forget_User_Click. La méthode doit maintenant ressembler à ce qui suit.

    private void Button_Forget_User_Click(object sender, RoutedEventArgs e)
    {
        //Remove it from Windows Hello
        MicrosoftPassportHelper.RemovePassportAccountAsync(_activeAccount);
    
        Debug.WriteLine("User " + _activeAccount.Username + " deleted.");
    
        //Navigate back to UserSelection page.
        Frame.Navigate(typeof(UserSelection));
    }
    
  • La méthode MicrosoftPassportHelper n’utilise pas AuthService pour supprimer le compte. Vous devez effectuer un appel à AuthService et passer l’id utilisateur.

    public static async void RemovePassportAccountAsync(UserAccount 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
            AuthService.AuthService.Instance.PassportRemoveUser(account.UserId);
        }
    
        //Then delete the account from the machines list of Passport Accounts
        await KeyCredentialManager.DeleteAsync(account.Username);
    }
    
  • Avant de pouvoir terminer l’implémentation de la classe de page d’accueil, vous devez créer une méthode dans MicrosoftPassportHelper.cs qui permettra à un appareil d’être supprimé. Créez une méthode qui appelle PassportRemoveDevice dans AuthService.

    public static void RemovePassportDevice(UserAccount account, Guid deviceId)
    {
        AuthService.AuthService.Instance.PassportRemoveDevice(account.UserId, deviceId);
    }
    
  • Dans Welcome.xaml.cs implémenter l’événement Forget Device Click. Cela utilise l’appareil sélectionné dans la liste des appareils et utilise l’assistance de passeport pour appeler l’appareil de suppression.

    private void Button_Forget_Device_Click(object sender, RoutedEventArgs e)
    {
        PassportDevice selectedDevice = UserListView.SelectedItem as PassportDevice;
        if (selectedDevice != null)
        {
            //Remove it from Windows Hello
            MicrosoftPassportHelper.RemovePassportDevice(_activeAccount, selectedDevice.DeviceId);
    
            Debug.WriteLine("User " + _activeAccount.Username + " deleted.");
    
            if (!UserListView.Items.Any())
            {
                //Navigate back to UserSelection page.
                Frame.Navigate(typeof(UserSelection));
            }
        }
        else
        {
            ForgetDeviceErrorTextBlock.Visibility = Visibility.Visible;
        }
    }
    
  • La page suivante que vous allez mettre à jour est la page UserSelection. La page UserSelection doit utiliser AuthService pour récupérer tous les comptes d’utilisateur de l’appareil actuel. Actuellement, il n’existe aucun moyen pour vous d’obtenir un ID d’appareil à transmettre à AuthService afin qu’il puisse retourner des comptes d’utilisateur pour cet appareil. Dans le dossier Utils, créez une classe appelée « Helpers.cs ». Modifiez la définition de classe pour qu’elle soit statique publique, puis ajoutez la méthode suivante qui vous permettra de récupérer l’ID d’appareil actuel.

    using System;
    using Windows.Security.ExchangeActiveSyncProvisioning;
    
    namespace PassportLogin.Utils
    {
        public static class Helpers
        {
            public static Guid GetDeviceId()
            {
                //Get the Device ID to pass to the server
                EasClientDeviceInformation deviceInformation = new EasClientDeviceInformation();
                return deviceInformation.Id;
            }
        }
    }
    
  • Dans la classe de page UserSelection, seul le code derrière doit changer, et non l’interface utilisateur. Dans UserSelection.xaml.cs mettez à jour la méthode chargée et la méthode de sélection utilisateur pour utiliser la classe UserAccount au lieu de la classe Account. Vous devez également obtenir tous les utilisateurs pour cet appareil via AuthService.

    using System.Linq;
    using PassportLogin.AuthService;
    
    namespace PassportLogin.Views
    {
        public sealed partial class UserSelection : Page
        {
            public UserSelection()
            {
                InitializeComponent();
                Loaded += UserSelection_Loaded;
            }
    
            private void UserSelection_Loaded(object sender, RoutedEventArgs e)
            {
                List<UserAccount> accounts = AuthService.AuthService.Instance.GetUserAccountsForDevice(Helpers.GetDeviceId());
    
                if (accounts.Any())
                {
                    UserListView.ItemsSource = accounts;
                    UserListView.SelectionChanged += UserSelectionChanged;
                }
                else
                {
                    //If there are no accounts navigate to the LoginPage
                    Frame.Navigate(typeof(Login));
                }
            }
    
            /// <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)
                {
                    UserAccount account = (UserAccount)((ListView)sender).SelectedValue;
                    if (account != null)
                    {
                        Debug.WriteLine("Account " + account.Username + " selected!");
                    }
                    Frame.Navigate(typeof(Login), account);
                }
            }
        }
    }
    
  • La page PassportRegister doit mettre à jour le code derrière, l’interface utilisateur n’a pas besoin de changer. Dans PassportRegister.xaml.cs supprimer la variable compte privé en haut de la classe, car elle n’est plus nécessaire. Mettez à jour l’événement RegisterButton click pour utiliser AuthService. Cette méthode crée un userAccount, puis tente de mettre à jour ses détails de passeport. Si passport ne parvient pas à créer une clé de passeport, le compte est supprimé à mesure que le processus d’inscription a échoué.

    private async void RegisterButton_Click_Async(object sender, RoutedEventArgs e)
    {
        ErrorMessage.Text = "";
    
        //Validate entered credentials are acceptable
        if (!string.IsNullOrEmpty(UsernameTextBox.Text))
        {
            //Register an Account on the AuthService so that we can get back a userId
            AuthService.AuthService.Instance.Register(UsernameTextBox.Text);
            Guid userId = AuthService.AuthService.Instance.GetUserId(UsernameTextBox.Text);
    
            if (userId != Guid.Empty)
            {
                //Now that the account exists on server try and create the necessary passport details and add them to the account
                bool isSuccessful = await MicrosoftPassportHelper.CreatePassportKeyAsync(userId, UsernameTextBox.Text);
                if (isSuccessful)
                {
                    //Navigate to the Welcome Screen. 
                    Frame.Navigate(typeof(Welcome), AuthService.AuthService.Instance.GetUserAccount(userId));
                }
                else
                {
                    //The passport account creation failed.
                    //Remove the account from the server as passport details were not configured
                    AuthService.AuthService.Instance.PassportRemoveUser(userId);
    
                    ErrorMessage.Text = "Account Creation Failed";
                }
            }
        }
        else
        {
            ErrorMessage.Text = "Please enter a username";
        }
    }
    
  • Générez et exécutez l’application (F5). Connectez-vous à l’exemple de compte d’utilisateur, avec les informations d’identification « sampleUsername » et « samplePassword ». Sur l’écran d’accueil, vous remarquerez peut-être que le bouton Oublier les appareils s’affiche, mais il n’y a aucun appareil. Lorsque vous créez ou migrez un utilisateur pour travailler avec Windows Hello, les informations de passeport ne sont pas envoyées à AuthService.

    Écran de connexion Windows Hello

    Connexion Windows Hello réussie

  • Pour obtenir les informations de passeport authService, le MicrosoftPassportHelper.cs doit être mis à jour. Dans la méthode CreatePassportKeyAsync, au lieu de retourner uniquement true dans le cas où elle réussit, vous devez appeler une nouvelle méthode qui tentera d’obtenir keyAttestation. Bien que cette pratique pratique n’enregistre pas ces informations dans AuthService, vous allez apprendre comment vous l’obtiendriez côté client. Mettez à jour la méthode CreatePassportKeyAsync.

    public static async Task<bool> CreatePassportKeyAsync(Guid userId, string username)
    {
        KeyCredentialRetrievalResult keyCreationResult = await KeyCredentialManager.RequestCreateAsync(username, KeyCredentialCreationOption.ReplaceExisting);
    
        switch (keyCreationResult.Status)
        {
            case KeyCredentialStatus.Success:
                Debug.WriteLine("Successfully made key");
                await GetKeyAttestationAsync(userId, keyCreationResult);
                return true;
            case KeyCredentialStatus.UserCanceled:
                Debug.WriteLine("User cancelled sign-in process.");
                break;
            case KeyCredentialStatus.NotFound:
                // User needs to setup Windows Hello
                Debug.WriteLine("Windows Hello is not setup!\nPlease go to Windows Settings and set up a PIN to use it.");
                break;
            default:
                break;
        }
    
        return false;
    }
    
  • Créez cette méthode GetKeyAttestationAsync dans MicrosoftPassportHelper.cs. Cette méthode montre comment obtenir toutes les informations nécessaires qui peuvent être fournies par Windows Hello pour chaque compte sur un appareil spécifique.

    using Windows.Storage.Streams;
    
    private static async Task GetKeyAttestationAsync(Guid userId, KeyCredentialRetrievalResult keyCreationResult)
    {
        KeyCredential userKey = keyCreationResult.Credential;
        IBuffer publicKey = userKey.RetrievePublicKey();
        KeyCredentialAttestationResult keyAttestationResult = await userKey.GetAttestationAsync();
        IBuffer keyAttestation = null;
        IBuffer certificateChain = null;
        bool keyAttestationIncluded = false;
        bool keyAttestationCanBeRetrievedLater = false;
        KeyCredentialAttestationStatus keyAttestationRetryType = 0;
    
        if (keyAttestationResult.Status == KeyCredentialAttestationStatus.Success)
        {
            keyAttestationIncluded = true;
            keyAttestation = keyAttestationResult.AttestationBuffer;
            certificateChain = keyAttestationResult.CertificateChainBuffer;
            Debug.WriteLine("Successfully made key and attestation");
        }
        else if (keyAttestationResult.Status == KeyCredentialAttestationStatus.TemporaryFailure)
        {
            keyAttestationRetryType = KeyCredentialAttestationStatus.TemporaryFailure;
            keyAttestationCanBeRetrievedLater = true;
            Debug.WriteLine("Successfully made key but not attestation");
        }
        else if (keyAttestationResult.Status == KeyCredentialAttestationStatus.NotSupported)
        {
            keyAttestationRetryType = KeyCredentialAttestationStatus.NotSupported;
            keyAttestationCanBeRetrievedLater = false;
            Debug.WriteLine("Key created, but key attestation not supported");
        }
    
        Guid deviceId = Helpers.GetDeviceId();
        //Update the Passport details with the information we have just gotten above.
        //UpdatePassportDetails(userId, deviceId, publicKey.ToArray(), keyAttestationResult);
    }
    
  • Vous avez peut-être remarqué dans la méthode GetKeyAttestationAsync que vous venez d’ajouter la dernière ligne a été commentée. Cette dernière ligne sera une nouvelle méthode que vous créez qui enverra toutes les informations Windows Hello à AuthService. Dans le monde réel, vous devez l’envoyer à un serveur réel avec une API web.

    using System.Runtime.InteropServices.WindowsRuntime;
    
    public static bool UpdatePassportDetails(Guid userId, Guid deviceId, byte[] publicKey, KeyCredentialAttestationResult keyAttestationResult)
    {
        //In the real world you would use an API to add Passport signing info to server for the signed in _account.
        //For this tutorial we do not implement a WebAPI for our server and simply mock the server locally 
        //The CreatePassportKey method handles adding the Windows Hello account locally to the device using the KeyCredential Manager
    
        //Using the userId the existing account should be found and updated.
        AuthService.AuthService.Instance.PassportUpdateDetails(userId, deviceId, publicKey, keyAttestationResult);
        return true;
    }
    
  • Supprimez les marques de commentaire de la dernière ligne de la méthode GetKeyAttestationAsync afin que les informations Windows Hello soient envoyées à AuthService.

  • Générez et exécutez l’application et connectez-vous avec les informations d’identification par défaut comme précédemment. Sur l’écran d’accueil, vous verrez maintenant que l’ID de l’appareil s’affiche. Si vous vous êtes connecté sur un autre appareil qui s’affiche également ici (si vous aviez un service d’authentification hébergé dans le cloud). Pour ce labo, l’ID d’appareil réel est affiché. Dans une implémentation réelle, vous souhaitez afficher un nom convivial qu’une personne peut comprendre et utiliser pour déterminer chaque appareil.

    Id d’appareil réussi de connexion Windows Hello

  • Pour effectuer cette tâche pratique, vous avez besoin d’une demande et d’un défi pour l’utilisateur lorsqu’il sélectionne dans la page de sélection de l’utilisateur et reconnectez-vous. AuthService a deux méthodes que vous avez créées pour demander un défi, une qui utilise un défi signé. Dans MicrosoftPassportHelper.cs créer une nouvelle méthode appelée « RequestSignAsync » Cette opération demande un défi à partir de l’AuthService, signez localement ce défi à l’aide d’une API Passport et envoyez le défi signé à AuthService. Dans ce labo, AuthService recevra le défi signé et retournera vrai. Dans une implémentation réelle, vous devez implémenter un mécanisme de vérification pour déterminer si le défi a été signé par l’utilisateur correct sur l’appareil approprié. Ajoutez la méthode ci-dessous au MicrosoftPassportHelper.cs

    private static async Task<bool> RequestSignAsync(Guid userId, KeyCredentialRetrievalResult openKeyResult)
    {
        // Calling userKey.RequestSignAsync() prompts the uses to enter the PIN or use Biometrics (Windows Hello).
        // The app would use the private key from the user account to sign the sign-in request (challenge)
        // The client would then send it back to the server and await the servers response.
        IBuffer challengeMessage = AuthService.AuthService.Instance.PassportRequestChallenge();
        KeyCredential userKey = openKeyResult.Credential;
        KeyCredentialOperationResult signResult = await userKey.RequestSignAsync(challengeMessage);
    
        if (signResult.Status == KeyCredentialStatus.Success)
        {
            // If the challenge from the server is signed successfully
            // send the signed challenge back to the server and await the servers response
            return AuthService.AuthService.Instance.SendServerSignedChallenge(
                userId, Helpers.GetDeviceId(), signResult.Result.ToArray());
        }
        else if (signResult.Status == KeyCredentialStatus.UserCanceled)
        {
            // User cancelled the Windows Hello PIN entry.
        }
        else if (signResult.Status == KeyCredentialStatus.NotFound)
        {
            // Must recreate Windows Hello key
        }
        else if (signResult.Status == KeyCredentialStatus.SecurityDeviceLocked)
        {
            // Can't use Windows Hello right now, remember that hardware failed and suggest restart
        }
        else if (signResult.Status == KeyCredentialStatus.UnknownError)
        {
            // Can't use Windows Hello right now, try again later
        }
    
        return false;
    }
    
  • Dans la classe MicrosoftPassportHelper, appelez la méthode RequestSignAsync à partir de la méthode GetPassportAuthenticationMessageAsync.

    public static async Task<bool> GetPassportAuthenticationMessageAsync(UserAccount 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.
    
            return await RequestSignAsync(account.UserId, openKeyResult);
        }
        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 CreatePassportKey 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 CreatePassportKey method will output that error.
            if (await CreatePassportKeyAsync(account.UserId, account.Username))
            {
                //If the Passport Key was again successfully created, Windows Hello has just been reset.
                //Now that the Passport Key has been reset for the _account retry sign in.
                return await GetPassportAuthenticationMessageAsync(account);
            }
        }
    
        // Can't use Windows Hello right now, try again later
        return false;
    }
    
  • Tout au long de cet exercice, vous avez mis à jour l’application côté client pour utiliser AuthService. En procédant ainsi, vous avez pu éliminer la nécessité de la classe Account et de la classe AccountHelper. Supprimez la classe Account, le dossier Models et la classe AccountHelper dans le dossier Utils. Vous devez supprimer toutes les références à l’espace de noms Models dans l’ensemble de l’application avant que la solution ne se génère correctement.

  • Générez et exécutez l’application et appréciez l’utilisation de Windows Hello avec le service et la base de données fictifs.

Dans ce labo, vous avez appris à utiliser les API Windows Hello pour remplacer la nécessité de mots de passe lors de l’authentification à partir d’un ordinateur Windows 10 ou Windows 11. Lorsque vous pensez à combien d’énergie est dépensée par les personnes qui conservent des mots de passe et prennent en charge les mots de passe perdus dans les systèmes existants, vous devez voir l’avantage de passer à ce nouveau système d’authentification Windows Hello.

Nous avons laissé en tant qu’exercice pour vous les détails de la façon dont vous allez implémenter l’authentification côté service et serveur. Il est prévu que la plupart de vous disposez de systèmes existants qui devront être migrés pour commencer à utiliser Windows Hello et les détails de chaque système diffèrent.