Membuat layanan login Windows Hello

Ini adalah Bagian 2 dari panduan lengkap tentang cara menggunakan Windows Hello sebagai alternatif untuk sistem autentikasi nama pengguna dan kata sandi tradisional di aplikasi Windows 10 UWP (universal Windows platform). Artikel ini mengambil tempat Bagian 1, Windows Hello aplikasi masuk, meninggalkan dan memperluas fungsionalitas untuk menunjukkan bagaimana Anda dapat mengintegrasikan Windows Hello ke dalam aplikasi yang ada.

Untuk membangun proyek ini, Anda memerlukan pengalaman dengan C#, dan XAML. Anda juga harus menggunakan Visual Studio 2015 (Edisi Komunitas atau lebih besar) pada komputer Windows 10.

Latihan 1: Logika Sisi Server

Dalam latihan ini Anda akan memulai dengan aplikasi Windows Hello yang dibangun di lab pertama dan membuat server dan database tiruan lokal. Tangan di lab ini dirancang untuk mengajarkan bagaimana Windows Hello dapat diintegrasikan ke dalam sistem yang ada. Dengan menggunakan server tiruan dan database tiruan, banyak pengaturan yang tidak terkait dihilangkan. Dalam aplikasi Anda sendiri, Anda harus mengganti objek tiruan dengan layanan dan database nyata.

  • Untuk memulai, buka solusi PassportLogin dari Passport Hands On Lab pertama.

  • Anda akan mulai dengan menerapkan server tiruan dan database tiruan. Buat folder baru bernama "AuthService". Di penjelajah solusi klik kanan pada solusi "PassportLogin (Universal Windows)" dan pilih Tambahkan > Folder Baru.

  • Buat kelas UserAccount dan PassportDevices yang akan bertindak sebagai model agar data disimpan dalam database tiruan. UserAccount akan mirip dengan model pengguna yang diterapkan pada server autentikasi tradisional. Klik kanan pada folder AuthService dan tambahkan kelas baru yang disebut "UserAccount.cs."

    Windows Hello authorization create folder

    Windows Hello authorization create class

  • Ubah definisi kelas menjadi publik lalu tambahkan properti publik berikut. Anda akan memerlukan referensi berikut.

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

    Anda mungkin telah memperhatikan daftar PassportDevices yang dikomentari. Ini adalah modifikasi yang perlu Anda lakukan pada model pengguna yang ada dalam implementasi Anda saat ini. Daftar PassportDevices akan berisi deviceID, kunci umum yang dibuat dari Windows Hello, dan KeyCredentialAttestationResult. Untuk tangan di lab ini, Anda harus menerapkan keyAttestationResult karena hanya disediakan oleh Windows Hello pada perangkat yang memiliki chip TPM (Modul Platform Tepercaya). KeyCredentialAttestationResult adalah kombinasi dari beberapa properti dan perlu dipisahkan untuk menyimpan dan memuatnya dengan database.

  • Buat kelas baru di folder AuthService yang disebut "PassportDevice.cs". Ini adalah model untuk perangkat Windows Hello seperti yang dibahas di atas. Ubah definisi kelas menjadi publik dan tambahkan properti berikut.

    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; }
        }
    }
    
  • Kembali ke di UserAccount.cs dan batalkan komentar daftar perangkat 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>();
        }
    }
    
  • Dengan model untuk UserAccount dan PassportDevice yang dibuat, Anda perlu membuat kelas baru lain di AuthService yang akan bertindak sebagai database tiruan. Karena ini adalah database tiruan tempat Anda akan menyimpan dan memuat daftar akun pengguna secara lokal. Di dunia nyata ini akan menjadi implementasi database Anda. Buat kelas baru di AuthService yang disebut "MockStore.cs". Ubah definisi kelas menjadi publik.

  • Karena penyimpanan tiruan akan menyimpan dan memuat daftar akun pengguna secara lokal, Anda dapat mengimplementasikan logika untuk menyimpan dan memuat daftar tersebut menggunakan XmlSerializer. Anda juga perlu mengingat nama file dan menyimpan lokasi. Di MockStore.cs terapkan hal berikut:

    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
        }
    }
  • Dalam metode pemuatan, Anda mungkin telah memperhatikan bahwa metode InitializeSampleUserAccounts dikomentari. Anda harus membuat metode ini di MockStore.cs. Metode ini akan mengisi daftar akun pengguna sehingga login dapat berlangsung. Di dunia nyata database pengguna sudah akan diisi. Dalam langkah ini Anda juga akan membuat konstruktor yang akan menginisialisasi daftar pengguna dan beban panggilan.

    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& lt; UserAccount & gt; ();
                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();
            }
        }
    }
    
  • Sekarang setelah metode InitalizeSampleUserAccounts ada uncomment panggilan metode dalam metode 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();
        }
    }
    
  • Daftar akun pengguna di penyimpanan tiruan sekarang dapat disimpan dan dimuat. Bagian lain dari aplikasi harus memiliki akses ke daftar ini sehingga perlu ada beberapa metode untuk mengambil data ini. Di bawah metode InitializeSampleUserAccounts, tambahkan metode get berikut. Mereka akan memungkinkan Anda untuk mendapatkan userid, satu pengguna, daftar pengguna untuk perangkat Windows Hello tertentu, dan juga mendapatkan kunci publik untuk pengguna di perangkat tertentu.

    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;
    }
    
  • Metode berikutnya yang akan diterapkan akan menangani operasi sederhana untuk menambahkan akun, menghapus akun, dan juga menghapus perangkat. Hapus perangkat diperlukan karena Windows Hello spesifik untuk perangkat. Untuk setiap perangkat tempat Anda masuk, pasangan kunci publik dan privat baru akan dibuat oleh Windows Hello. Ini seperti memiliki kata sandi yang berbeda untuk setiap perangkat yang Anda masuki, satu-satunya hal adalah Anda tidak perlu mengingat semua kata sandi yang dilakukan server. Tambahkan metode berikut ke 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;
    }
    
  • Di kelas MockStore tambahkan metode yang akan menambahkan informasi terkait Windows Hello ke UserAccount yang ada. Metode ini akan disebut PassportUpdateDetails dan akan mengambil parameter untuk mengidentifikasi pengguna, dan detail Windows Hello. KeyAttestationResult telah dikomentari saat membuat PassportDevice, dalam aplikasi dunia nyata, Anda akan memerlukan ini.

    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();
    }
    
  • Kelas MockStore sekarang selesai, karena ini mewakili database yang harus dianggap privat. Untuk mengakses kelas MockStore, AuthService diperlukan untuk memanipulasi data database. Di folder AuthService, buat kelas baru yang disebut "AuthService.cs". Ubah definisi kelas menjadi publik dan tambahkan pola instans singleton untuk memastikan hanya satu instans yang pernah dibuat.

    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()
            { }
        }
    }
    
  • Kelas AuthService perlu membuat instans kelas MockStore dan menyediakan akses ke properti objek 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 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);
            }
        }
    }
    
  • Anda memerlukan metode di kelas AuthService untuk mengakses metode menambahkan, menghapus, dan memperbarui detail paspor di objek MockStore. Di akhir file kelas AuthService, tambahkan metode berikut.

    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);
    }
    
  • Kelas AuthService harus menyediakan metode untuk memvalidasi kredensial. Metode ini akan mengambil nama pengguna dan kata sandi dan memastikan bahwa akun ada dan kata sandi valid. Sistem yang ada akan memiliki metode yang setara dengan ini yang memeriksa pengguna diotorisasi. Tambahkan ValidateCredentials berikut ke file 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;
    }
    
  • Kelas AuthService memerlukan metode tantangan permintaan yang akan mengembalikan tantangan kepada klien untuk memvalidasi pengguna adalah siapa yang mereka klaim. Kemudian metode diperlukan di kelas AuthService untuk menerima tantangan yang ditandatangani kembali dari klien. Untuk tangan di lab ini metode bagaimana Anda menentukan apakah tantangan yang ditandatangani telah selesai telah dibiarkan tidak lengkap. Setiap implementasi Windows Hello ke dalam sistem autentikasi yang ada akan sedikit berbeda. Kunci publik yang disimpan di server perlu dicocokkan dengan hasil yang dikembalikan klien ke server. Tambahkan kedua metode ini ke 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;
    }
    

Latihan 2: Logika Sisi Klien

Dalam latihan ini Anda akan mengubah tampilan sisi klien dan kelas pembantu dari lab pertama untuk menggunakan kelas AuthService. Di dunia nyata, AuthService akan menjadi server autentikasi dan Anda harus menggunakan Web API untuk mengirim dan menerima data dari server. Untuk tangan ini pada klien lab dan server semuanya lokal untuk menjaga hal-hal sederhana. Tujuannya adalah untuk mempelajari cara menggunakan API Windows Hello.

  • Di MainPage.xaml.cs Anda dapat menghapus panggilan metode AccountHelper.LoadAccountListAsync dalam metode yang dimuat karena kelas AuthService membuat instans MockStore yang memuat daftar akun. Metode yang dimuat sekarang akan terlihat seperti di bawah ini. Perhatikan definisi metode asinkron dihapus karena tidak ada yang sedang ditunggu.

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        Frame.Navigate(typeof(UserSelection));
    }
    
  • Perbarui antarmuka halaman Masuk untuk mengharuskan paspor dimasukkan. Tangan di lab ini menunjukkan bagaimana sistem yang ada dapat dimigrasikan untuk menggunakan Windows Hello dan akun yang ada akan memiliki nama pengguna dan kata sandi. Perbarui juga penjelasan di bagian bawah XAML untuk menyertakan kata sandi default. Perbarui XAML berikut di 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>
    
  • Dalam kode kelas Login di belakang Anda harus mengubah variabel privat Akun di bagian atas kelas menjadi UserAccount. Ubah peristiwa OnNavigateTo untuk melemparkan jenis menjadi UserAccount. Anda akan memerlukan referensi berikut.

    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;
                        SignInPassport();
                    }
                }
            }
        }
    }
    
  • Karena halaman Masuk menggunakan objek UserAccount alih-alih objek Akun sebelumnya, MicrosoftPassportHelper.cs perlu diperbarui untuk menggunakan UserAccount sebagai parameter untuk beberapa metode. Anda harus mengubah parameter berikut untuk metode CreatePassportKeyAsync, RemovePassportAccountAsync dan GetPassportAuthenticationMessageAsync. Karena kelas UserAccount memiliki Guid untuk UserId, Anda akan mulai menggunakan Id di tempat yang lebih spesifik.

    public static async Task<bool> CreatePassportKeyAsync(Guid userId, string username)
    {
        KeyCredentialRetrievalResult keyCreationResult = await KeyCredentialManager.RequestCreateAsync(username, KeyCredentialCreationOption.ReplaceExisting);
    }
    
    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;
    }
    
  • Metode SignInPassport dalam file Login.xaml.cs perlu diperbarui untuk menggunakan AuthService alih-alih AccountHelper. Validasi kredensial akan terjadi melalui AuthService. Untuk tangan ini di lab, satu-satunya akun yang dikonfigurasi adalah "sampleUsername". Akun ini dibuat dalam metode InitializeSampleUserAccounts di MockStore.cs. Perbarui metode SignInPassport di Login.xaml.cs sekarang untuk mencerminkan cuplikan kode di bawah ini.

    private async void SignInPassportAsync()
    {
        if (_isExistingLocalAccount)
        {
            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";
        }
    }
    
  • Karena Windows Hello akan membuat pasangan kunci publik dan privat yang berbeda untuk setiap akun di setiap perangkat, halaman Selamat Datang perlu menampilkan daftar perangkat terdaftar untuk akun yang masuk, dan memungkinkan masing-masing untuk dilupakan. Di Welcome.xaml tambahkan XAML berikut di bawah ForgetButton. Ini akan mengimplementasikan tombol lupa perangkat, area teks kesalahan, dan daftar untuk menampilkan semua perangkat.

    <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" Foreground="Black"/>
    
        <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>
    
  • Dalam file Welcome.xaml.cs, Anda harus mengubah variabel Akun privat di bagian atas kelas menjadi variabel UserAccount privat. Kemudian perbarui metode OnNavigatedTo untuk menggunakan AuthService dan mengambil informasi untuk akun saat ini. Ketika Anda memiliki informasi akun, Anda dapat mengatur sumber item daftar untuk menampilkan perangkat. Anda harus menambahkan referensi ke namespace layanan 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;
                    }
                }
            }
        }
    }
    
  • Karena Anda akan menggunakan AuthService saat menghapus akun, referensi ke AccountHelper dalam metode Button_Forget_User_Click dapat dihapus. Metode sekarang akan terlihat seperti di bawah ini.

    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));
    }
    
  • Metode MicrosoftPassportHelper tidak menggunakan AuthService untuk menghapus akun. Anda perlu melakukan panggilan ke AuthService dan meneruskan userId.

    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);
    }
    
  • Sebelum Anda dapat menyelesaikan penerapan kelas halaman Selamat Datang, Anda perlu membuat metode di MicrosoftPassportHelper.cs yang akan memungkinkan perangkat dihapus. Buat metode baru yang akan memanggil PassportRemoveDevice di AuthService.

    public static void RemovePassportDevice(UserAccount account, Guid deviceId)
    {
        AuthService.AuthService.Instance.PassportRemoveDevice(account.UserId, deviceId);
    }
    
  • Di Welcome.xaml.cs terapkan peristiwa klik Lupakan Perangkat. Ini akan menggunakan perangkat yang dipilih dari daftar perangkat dan menggunakan pembantu paspor untuk memanggil hapus perangkat.

    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;
        }
    }
    
  • Halaman berikutnya yang akan Anda perbarui adalah halaman UserSelection. Halaman UserSelection harus menggunakan AuthService untuk mengambil semua akun pengguna untuk perangkat saat ini. Saat ini tidak ada cara bagi Anda untuk mendapatkan id perangkat untuk diteruskan ke AuthService sehingga dapat mengembalikan akun pengguna untuk perangkat tersebut. Di folder Utils buat kelas baru yang disebut "Helpers.cs". Ubah definisi kelas menjadi statis publik lalu tambahkan metode berikut yang akan memungkinkan Anda untuk mengambil id perangkat saat ini.

    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;
            }
        }
    }
    
  • Di kelas halaman UserSelection hanya kode di belakang yang perlu diubah, bukan antarmuka pengguna. Di UserSelection.xaml.cs, perbarui metode yang dimuat dan metode pemilihan pengguna untuk menggunakan kelas UserAccount alih-alih kelas Akun. Anda juga perlu mendapatkan semua pengguna untuk perangkat ini melalui 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);
                }
            }
        }
    }
    
  • Halaman PassportRegister perlu memperbarui kode di belakang, antarmuka pengguna tidak perlu berubah. Di PassportRegister.xaml.cs hapus variabel Akun privat di bagian atas kelas karena tidak lagi diperlukan. Perbarui peristiwa klik RegisterButton untuk menggunakan AuthService. Metode ini akan membuat UserAccount baru lalu mencoba dan memperbarui detail paspornya. Jika paspor gagal membuat kunci paspor, akun akan dihapus saat proses pendaftaran gagal.

    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";
        }
    }
    
  • Membangun dan menjalankan aplikasi (F5). Masuk ke akun pengguna sampel, dengan kredensial "sampleUsername" dan "samplePassword". Pada layar selamat datang, Anda mungkin melihat tombol Lupakan perangkat ditampilkan tetapi tidak ada perangkat. Saat Anda membuat atau memigrasikan pengguna untuk bekerja dengan Windows Hello informasi paspor tidak didorong ke AuthService.

    Windows Hello login screen

    Windows Hello login successful

  • Untuk mendapatkan informasi paspor ke AuthService, MicrosoftPassportHelper.cs perlu diperbarui. Dalam metode CreatePassportKeyAsync, alih-alih hanya mengembalikan true jika berhasil, Anda harus memanggil metode baru yang akan mencoba mendapatkan KeyAttestation. Meskipun tangan di lab ini tidak merekam informasi ini di AuthService, Anda akan mempelajari bagaimana Anda akan mendapatkan informasi ini di sisi klien. Perbarui metode 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;
    }
    
  • Buat metode GetKeyAttestationAsync ini di MicrosoftPassportHelper.cs. Metode ini akan menunjukkan cara mendapatkan semua informasi yang diperlukan yang dapat disediakan oleh Windows Hello untuk setiap akun pada perangkat tertentu.

    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 Pasport details with the information we have just gotten above.
        //UpdatePassportDetails(userId, deviceId, publicKey.ToArray(), keyAttestationResult);
    }
    
  • Anda mungkin telah memperhatikan dalam metode GetKeyAttestationAsync bahwa Anda baru saja menambahkan baris terakhir yang dikomentari. Baris terakhir ini akan menjadi metode baru yang Anda buat yang akan mengirim semua informasi Windows Hello ke AuthService. Di dunia nyata Anda harus mengirim ini ke server aktual dengan 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;
    }
    
  • Hapus komentar baris terakhir dalam metode GetKeyAttestationAsync sehingga informasi Windows Hello dikirim ke AuthService.

  • Buat dan jalankan aplikasi dan masuk dengan kredensial default seperti sebelumnya. Pada layar selamat datang, Anda sekarang akan melihat bahwa Id perangkat ditampilkan. Jika Anda masuk di perangkat lain yang juga akan ditampilkan di sini (jika Anda memiliki layanan autentikasi yang dihosting cloud). Untuk tangan ini di lab, Id perangkat aktual sedang ditampilkan. Dalam implementasi nyata, Anda ingin menampilkan nama yang ramah yang dapat dipahami dan digunakan seseorang untuk menentukan setiap perangkat.

    Windows Hello login successful device id

    1. Untuk menyelesaikan tangan ini di lab, Anda memerlukan permintaan dan tantangan bagi pengguna ketika mereka memilih dari halaman pilihan pengguna dan masuk kembali. AuthService memiliki dua metode yang Anda buat untuk meminta tantangan, yang menggunakan tantangan yang ditandatangani. Di MicrosoftPassportHelper.cs, buat metode baru yang disebut "RequestSignAsync" Ini akan meminta tantangan dari AuthService, menandatangani tantangan tersebut secara lokal menggunakan PASSPORT API dan mengirim tantangan yang ditandatangani ke AuthService. Di lab ini, AuthService akan menerima tantangan yang ditandatangani dan mengembalikan true. Dalam implementasi aktual, Anda perlu menerapkan mekanisme verifikasi untuk menentukan apakah tantangan ditandatangani oleh pengguna yang benar pada perangkat yang benar. Tambahkan metode di bawah ini ke 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;
    }
    
    1. Di kelas MicrosoftPassportHelper, panggil metode RequestSignAsync dari metode 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;
    }
    
  • Sepanjang latihan ini, Anda telah memperbarui aplikasi sisi klien untuk menggunakan AuthService. Dengan melakukan ini, Anda telah dapat menghilangkan kebutuhan akan kelas Akun dan kelas AccountHelper. Hapus kelas Akun, folder Model, dan kelas AccountHelper di folder Utils. Anda harus menghapus semua referensi ke namespace layanan Model di seluruh aplikasi sebelum solusi berhasil dibangun.

  • Bangun dan jalankan aplikasi dan nikmati penggunaan Windows Hello dengan layanan tiruan dan database.

Di lab ini, Anda telah mempelajari cara menggunakan API Windows Hello untuk menggantikan kebutuhan kata sandi saat menggunakan autentikasi dari komputer Windows 10. Ketika Anda mempertimbangkan berapa banyak energi yang dikeluarkan oleh orang-orang yang mempertahankan kata sandi dan mendukung kata sandi yang hilang dalam sistem yang ada, Anda akan melihat manfaat pindah ke sistem autentikasi Windows Hello baru ini.

Kami telah meninggalkan sebagai latihan untuk Anda detail tentang bagaimana Anda akan menerapkan autentikasi di sisi layanan dan server. Diharapkan bahwa sebagian besar dari Anda akan memiliki sistem yang ada yang perlu dimigrasikan untuk mulai bekerja dengan Windows Hello dan detail setiap sistem akan berbeda.