Web 帳戶管理員

本文說明如何使用 AccountsSettingsPane 透過 Windows 10 和 Windows 11 Web 帳戶管理器 API,將通用 Windows 平台 (UWP) 應用程式連接到外部身分識別提供者 (例如 Microsoft 或 Facebook)。 您將了解如何要求使用者使用其 Microsoft 帳戶的權限、取得存取權杖,以及使用它執行基本作業 (例如取得設定檔資料或將檔案上傳到其 OneDrive 帳戶)。 這些步驟與透過支援 Web 帳戶管理員的身分識別提供者取得使用者權限和存取權類似。

注意

如需完整的程式碼範例,請參閱 GitHub 上的 WebAccountManagement 範例

開始設定

首先,在 Visual Studio 中建立一個新的空白應用程式。

其次,若要連接到身分識別提供者,您需要將應用程式與商店關聯。 若要這樣做,請以滑鼠右鍵按一下您的專案,選擇 Store/Publish>Associate 應用程式與商店,然後按照精靈的說明進行操作。

第三,建立一個非常基本的 UI,其中包含一個簡單的 XAML 按鈕和兩個文字方塊。

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
	<Button x:Name="LoginButton" Content="Log in" Click="LoginButton_Click" />
	<TextBlock x:Name="UserIdTextBlock"/>
	<TextBlock x:Name="UserNameTextBlock"/>
</StackPanel>

並在程式碼後置中附加到按鈕的事件處理常式:

private void LoginButton_Click(object sender, RoutedEventArgs e)
{	
}

最後,新增以下命名空間,這樣您之後就不必擔心任何參考問題:

using System;
using Windows.Security.Authentication.Web.Core;
using Windows.System;
using Windows.UI.ApplicationSettings;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.Data.Json;
using Windows.UI.Xaml.Navigation;
using Windows.Web.Http;

顯示帳戶設定窗格

系統提供了一個名為 AccountsSettingsPane 的內建使用者介面,用於管理身分識別提供者和 Web 帳戶。 您可以將其顯示如下:

private void LoginButton_Click(object sender, RoutedEventArgs e)
{
	AccountsSettingsPane.Show(); 
}

如果您執行應用程式並點擊「登入」按鈕,它應該會顯示一個空白視窗。

Screenshot of the Choose an account window with no accounts listed.

此窗格是空的,因為系統僅提供 UI shell - 由開發人員以程式設計方式使用身分識別提供者填入該窗格。

提示

或者,您可以使用 ShowAddAccountAsync 而不是 Show,後者會傳回 IAsyncAction 來查詢作業的狀態。

註冊 AccountCommandsRequested

若要將命令新增至窗格,我們要先註冊 AccountCommandsRequested 事件處理常式。 這會告訴系統在使用者要求查看窗格時,執行我們的建置邏輯 (例如,按一下我們的 XAML 按鈕)。

在程式碼後置中,覆寫 OnNavigatedTo 和 OnNavigatedFrom 事件,並在其中加入以下程式碼:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
	AccountsSettingsPane.GetForCurrentView().AccountCommandsRequested += BuildPaneAsync; 
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
	AccountsSettingsPane.GetForCurrentView().AccountCommandsRequested -= BuildPaneAsync; 
}

使用者不會經常與帳戶互動,因此以這種方式註冊和取消註冊事件處理常式有助於防止記憶體流失。 如此一來,您的自定義窗格只會在使用者要求它時處於記憶體中 (例如,因為他們位於「設定」或「登入」頁面上)。

建置帳戶設定窗格

只要顯示 AccountsSettingsPane,就會呼叫 BuildPaneAsync 方法。 我們將在此處放置程式碼來自訂窗格中顯示的命令。

先取得延遲。 這會告訴系統延遲顯示 AccountsSettingsPane,直到我們完成建置為止。

private async void BuildPaneAsync(AccountsSettingsPane s,
	AccountsSettingsPaneCommandsRequestedEventArgs e)
{
	var deferral = e.GetDeferral();
		
	deferral.Complete(); 
}

接下來,使用 WebAuthenticationCoreManager.FindAccountProviderAsync 方法取得提供者。 提供者的 URL 會根據提供者而有所不同,其可以在提供者的文件中找到。 對於 Microsoft 帳戶和 Azure Active Directory,其為「https://login.microsoft.com"。

private async void BuildPaneAsync(AccountsSettingsPane s,
	AccountsSettingsPaneCommandsRequestedEventArgs e)
{
	var deferral = e.GetDeferral();
		
	var msaProvider = await WebAuthenticationCoreManager.FindAccountProviderAsync(
		"https://login.microsoft.com", "consumers"); 
		
	deferral.Complete(); 
}

請注意,我們也會將字串「consumers」傳遞給選擇性授權單位參數。 這是因為 Microsoft 提供了兩種不同類型的驗證 - 「消費者」適用的 Microsoft 帳戶 (MSA) 和「組織」適用的 Azure Active Directory (AAD)。 「消費者」授權單位表示我們需要 MSA 選項。 如果您要開發企業應用程式,請改用字串「organizations」。

最後,透過建立一個新的 WebAccountProviderCommand 將提供者新增至 AccountsSettingsPane,如下所示:

private async void BuildPaneAsync(AccountsSettingsPane s,
	AccountsSettingsPaneCommandsRequestedEventArgs e)
{
	var deferral = e.GetDeferral();

	var msaProvider = await WebAuthenticationCoreManager.FindAccountProviderAsync(
		"https://login.microsoft.com", "consumers");

	var command = new WebAccountProviderCommand(msaProvider, GetMsaTokenAsync);  

	e.WebAccountProviderCommands.Add(command);

	deferral.Complete(); 
}

我們傳遞到新 WebAccountProviderCommand 的 GetMsaToken 方法尚不存在 (我們將在下一步中建置它),因此現在可以隨意將其新增為空方法。

執行上述程式碼,您的窗格應如下所示:

Screenshot of the Choose an account window with accounts listed.

要求權杖

一旦我們在 AccountsSettingsPane 中顯示了 Microsoft 帳戶選項,我們就需要處理使用者選擇它時會發生的情況。 我們註冊了 GetMsaToken 方法,以便在使用者選擇使用其 Microsoft 帳戶登入時觸發,因此我們將在那裡取得權杖。

若要取得權杖,請使用 RequestTokenAsync 方法,如下所示:

private async void GetMsaTokenAsync(WebAccountProviderCommand command)
{
	WebTokenRequest request = new WebTokenRequest(command.WebAccountProvider, "wl.basic");
	WebTokenRequestResult result = await WebAuthenticationCoreManager.RequestTokenAsync(request);
}

在此範例中,我們會將字串「wl.basic」傳遞給範圍參數。 範圍代表您從特定使用者的提供服務要求的資訊類型。 某些範圍僅提供對使用者基本資訊 (如姓名和電子郵件地址) 的存取權,而其他範圍可能會授予對敏感資訊 (如使用者照片或電子郵件收件匣) 的存取權。 一般來說,您的應用程式應該使用最寬鬆的範圍來實現其功能。 服務提供者將提供有關取得用於其服務之權杖所需範圍的文件。

提示

或者,如果您的應用程式使用登入提示 (使用預設電子郵件地址填入使用者欄位) 或與登入體驗相關的其他特殊屬性,請將其列在 WebTokenRequest.AppProperties 屬性中。 這將導致系統在快取 Web 帳戶時忽略該屬性,從而防止快取中的帳戶不符。

如果您要開發企業應用程式,您可能需要連接到 Azure Active Directory (AAD) 實例,並使用 Microsoft Graph API 而不是一般 MSA 服務。 在這種情況下,請改用以下程式碼:

private async void GetAadTokenAsync(WebAccountProviderCommand command)
{
	string clientId = "your_guid_here"; // Obtain your clientId from the Azure Portal
	WebTokenRequest request = new WebTokenRequest(provider, "User.Read", clientId);
	request.Properties.Add("resource", "https://graph.microsoft.com");
	WebTokenRequestResult result = await WebAuthenticationCoreManager.RequestTokenAsync(request);
}

本文的其餘部分會繼續描述 MSA 案例,但 AAD 的程式碼非常相似。 有關 AAD/Graph 的更多資訊,包括 GitHub 上的完整範例,請參閱 Microsoft Graph 文件

使用權杖

RequestTokenAsync 方法會傳回 WebTokenRequestResult 物件,其中包含您要求的結果。 如果您的要求成功,它將包含權杖。

private async void GetMsaTokenAsync(WebAccountProviderCommand command)
{
	WebTokenRequest request = new WebTokenRequest(command.WebAccountProvider, "wl.basic");
	WebTokenRequestResult result = await WebAuthenticationCoreManager.RequestTokenAsync(request);
	
	if (result.ResponseStatus == WebTokenRequestStatus.Success)
	{
		string token = result.ResponseData[0].Token; 
	}
}

注意

如果您在要求權杖時收到錯誤,請確保您已按照第一步所述將您的應用程式與應用程式商店相關聯。 如果您跳過此步驟,您的應用程式將無法取得權杖。

獲得權杖後,您可以使用它來呼叫提供者的 API。 在下面的程式碼中,我們將呼叫使用者資訊 Microsoft Live API 來取得有關使用者的基本資訊,並將其顯示在我們的 UI 中。 但請注意,在大多數情況下,建議您儲存獲得的權杖,然後在個別的方法中使用它。

private async void GetMsaTokenAsync(WebAccountProviderCommand command)
{
	WebTokenRequest request = new WebTokenRequest(command.WebAccountProvider, "wl.basic");
	WebTokenRequestResult result = await WebAuthenticationCoreManager.RequestTokenAsync(request);
	
	if (result.ResponseStatus == WebTokenRequestStatus.Success)
	{
		string token = result.ResponseData[0].Token; 
		
		var restApi = new Uri(@"https://apis.live.net/v5.0/me?access_token=" + token);

		using (var client = new HttpClient())
		{
			var infoResult = await client.GetAsync(restApi);
			string content = await infoResult.Content.ReadAsStringAsync();

			var jsonObject = JsonObject.Parse(content);
			string id = jsonObject["id"].GetString();
			string name = jsonObject["name"].GetString();

			UserIdTextBlock.Text = "Id: " + id; 
			UserNameTextBlock.Text = "Name: " + name;
		}
	}
}

呼叫各種 REST API 的方式因提供者而異; 有關如何使用權杖的資訊,請參閱提供者的 API 文件。

儲存帳戶以供日後使用

權杖對於立即獲取有關使用者的資訊非常有用,但它們通常具有不同的生命週期 - 例如,MSA 權杖僅在幾個小時內有效。 幸運的是,您不需要在每次權杖過期時重新顯示 AccountsSettingsPane。 一旦使用者授權您的應用程式一次,您就可以儲存使用者的帳戶資訊以供將來使用。

若要這樣做,請使用 WebAccount 類別。 透過與要求權杖相同的方法返回 WebAccount

private async void GetMsaTokenAsync(WebAccountProviderCommand command)
{
	WebTokenRequest request = new WebTokenRequest(command.WebAccountProvider, "wl.basic");
	WebTokenRequestResult result = await WebAuthenticationCoreManager.RequestTokenAsync(request);
	
	if (result.ResponseStatus == WebTokenRequestStatus.Success)
	{
		WebAccount account = result.ResponseData[0].WebAccount; 
	}
}

當您擁有 WebAccount 實例後,就可以輕鬆儲存它。 在以下範例中,我們使用 LocalSettings。 有關使用 LocalSettings 和其他方法儲存使用者資料的詳細資訊,請參閱儲存和擷取應用程式設定和資料

private async void StoreWebAccount(WebAccount account)
{
	ApplicationData.Current.LocalSettings.Values["CurrentUserProviderId"] = account.WebAccountProvider.Id;
	ApplicationData.Current.LocalSettings.Values["CurrentUserId"] = account.Id; 
}

然後,我們可以使用如下所示的非同步方法,嘗試使用儲存的 WebAccount 在背景取得權杖。

private async Task<string> GetTokenSilentlyAsync()
{
	string providerId = ApplicationData.Current.LocalSettings.Values["CurrentUserProviderId"]?.ToString();
	string accountId = ApplicationData.Current.LocalSettings.Values["CurrentUserId"]?.ToString();

	if (null == providerId || null == accountId)
	{
		return null; 
	}

	WebAccountProvider provider = await WebAuthenticationCoreManager.FindAccountProviderAsync(providerId);
	WebAccount account = await WebAuthenticationCoreManager.FindAccountAsync(provider, accountId);

	WebTokenRequest request = new WebTokenRequest(provider, "wl.basic");

	WebTokenRequestResult result = await WebAuthenticationCoreManager.GetTokenSilentlyAsync(request, account);
	if (result.ResponseStatus == WebTokenRequestStatus.UserInteractionRequired)
	{
		// Unable to get a token silently - you'll need to show the UI
		return null; 
	}
	else if (result.ResponseStatus == WebTokenRequestStatus.Success)
	{
		// Success
		return result.ResponseData[0].Token;
	}
	else
	{
		// Other error 
		return null; 
	}
}

將上述方法放在建置 AccountsSettingsPane 的程式碼之前。 如果權杖是在背景取得的,則無需顯示該窗格。

private void LoginButton_Click(object sender, RoutedEventArgs e)
{
	string silentToken = await GetMsaTokenSilentlyAsync();

	if (silentToken != null)
	{
		// the token was obtained. store a reference to it or do something with it here.
	}
	else
	{
		// the token could not be obtained silently. Show the AccountsSettingsPane
		AccountsSettingsPane.Show();
	}
}

由於以無訊息方式取得權杖非常簡單,因此您應該使用此程序在工作階段之間重新整理權杖,而不是快取現有權杖 (因為該權杖隨時可能過期)。

注意

上述範例只涵蓋了基本的成功和失敗案例。 您的應用程式還應該考慮異常情況 (例如,使用者撤銷您的應用程式的權限,或從 Windows 中移除其帳戶),並妥善處理這些情況。

移除已儲存的帳戶

如果您保留 Web 帳戶,您可能想要讓使用者能夠將其帳戶與您的應用程式解除關聯。 如此一來,他們就可以有效地「登出」應用程式:他們的帳戶資訊將不再在啟動時自動載入。 若要這樣做,請先從存放區中移除所有已儲存的帳戶和提供者資訊。 然後呼叫 SignOutAsync 清除快取,並使應用程式可能擁有的所有現有權杖失效。

private async Task SignOutAccountAsync(WebAccount account)
{
	ApplicationData.Current.LocalSettings.Values.Remove("CurrentUserProviderId");
	ApplicationData.Current.LocalSettings.Values.Remove("CurrentUserId"); 
	account.SignOutAsync(); 
}

新增不支援 WebAccountManager 的提供者

如果您想將某個服務的驗證整合到您的應用程式中,但該服務不支援 WebAccountManager (例如 Google+ 或 Twitter),您仍然可以手動將該提供者新增至 AccountsSettingsPane 中。 若要這樣做,請建立一個新的 WebAccountProvider 物件,並提供您自己的名稱和 .png 圖示,然後將其新增至 WebAccountProviderCommands 清單中。 這是一些虛設常式程式碼:

private async void BuildPaneAsync(AccountsSettingsPane s, AccountsSettingsPaneCommandsRequestedEventArgs e)
{
	// other code here 

	var twitterProvider = new WebAccountProvider("twitter", "Twitter", new Uri(@"ms-appx:///Assets/twitter-auth-icon.png")); 
	var twitterCmd = new WebAccountProviderCommand(twitterProvider, GetTwitterTokenAsync);
	e.WebAccountProviderCommands.Add(twitterCmd);	
	
	// other code here
}

private async void GetTwitterTokenAsync(WebAccountProviderCommand command)
{
	// Manually handle Twitter login here
}

注意

這只會為 AccountsSettingsPane 新增一個圖示,並在點擊該圖示時執行您指定的方法 (在本例中為 GetTwitterTokenAsync)。 您必須提供處理實際驗證的程式碼。 如需詳細資訊,請參閱 Web 驗證代理人,其提供使用 REST 服務進行驗證的輔助方法。

新增自訂標頭

您可以使用 HeaderText 屬性自訂帳戶設定窗格,如下所示:

private async void BuildPaneAsync(AccountsSettingsPane s, AccountsSettingsPaneCommandsRequestedEventArgs e)
{
	// other code here 
	
	args.HeaderText = "MyAwesomeApp works best if you're signed in."; 	
	
	// other code here
}

Screenshot of the Choose an account window with no accounts listed and a message that says My Awesome App works best if you're signed in.

請勿過度使用標頭文字; 保持簡潔扼要。 如果您的登入過程很複雜並且需要顯示更多資訊,請使用自訂連結將使用者連結到個別頁面。

您可以將自訂指令新增至 AccountsSettingsPane,其會做為連結顯示在 WebAccountProviders 下方。 自訂命令非常適合與使用者帳戶相關的簡單工作,例如顯示隱私原則,或為遇到問題的使用者啟動支援頁面。

以下是範例:

private async void BuildPaneAsync(AccountsSettingsPane s, AccountsSettingsPaneCommandsRequestedEventArgs e)
{
	// other code here 
	
	var settingsCmd = new SettingsCommand(
		"settings_privacy", 
		"Privacy policy", 
		async (x) => await Launcher.LaunchUriAsync(new Uri(@"https://privacy.microsoft.com/en-US/"))); 

	e.Commands.Add(settingsCmd); 
	
	// other code here
}

Screenshot of the Choose an account window with no accounts listed and a link to a Privacy policy.

理論上,您可以使用設定命令處理一切。 但是,我們建議只在如上所述,與帳戶相關的場景中使用設定命令。

另請參閱

Windows.Security.Authentication.Web.Core 命名空間

Windows.Security.Credentials 命名空間

AccountsSettingsPane 類別

Web 驗證代理人

Web 帳戶管理範例

Lunch Scheduler 應用程式