Web API を呼び出すデスクトップ アプリ: WAM を使用してトークンを取得する
Microsoft Authentication Library (MSAL) は、認証ブローカーとして機能する Windows 10+ コンポーネントである Web アカウント マネージャー (WAM) を呼び出します。 ブローカーによって、アプリのユーザーは、Windows で認識されているアカウント (Windows セッションへとサインインしたアカウントなど) との統合による恩恵を受けられるようになります。
WAM の価値提案
WAM などの認証ブローカーを使用することで、次のような多くの利点があります。
- セキュリティの強化。 「トークン保護」を参照してください。
- Windows Hello、条件付きアクセス、FIDO キーのサポート。
- Windows の [電子メール アカウント] ビューとの統合。
- 高速のシングル サインオン。
- 現在の Windows アカウントを使用してサイレントにサインインする機能。
- バグ修正と機能強化が Windows に付属して提供される。
WAM の制限事項
- WAM は Windows 10 以降と Windows Server 2019 以降で使用できます。 Mac、Linux、以前のバージョンの Windows では、MSAL はブラウザーに自動的にフォールバックします。
- Azure Active Directory B2C (Azure AD B2C) およびActive Directory フェデレーション サービス (AD FS) オーソリティはサポートされていません。 MSAL はブラウザーにフォールバックします。
WAM 統合パッケージ
ほとんどのアプリでは、この統合を使用するには Microsoft.Identity.Client.Broker
パッケージを参照する必要があります。 .NET MAUI アプリでは、これを行う必要はありません。ターゲットが net6-windows
以降である場合、この機能は MSAL の内部にあります。
WAM 呼び出しパターン
WAM については、次のパターンを使用できます。
// 1. Configuration - read below about redirect URI
var pca = PublicClientApplicationBuilder.Create("client_id")
.WithBroker(new BrokerOptions(BrokerOptions.OperatingSystems.Windows))
.Build();
// Add a token cache; see https://learn.microsoft.com/azure/active-directory/develop/msal-net-token-cache-serialization?tabs=desktop
// 2. Find an account for silent login
// Is there an account in the cache?
IAccount accountToLogin = (await pca.GetAccountsAsync()).FirstOrDefault();
if (accountToLogin == null)
{
// 3. No account in the cache; try to log in with the OS account
accountToLogin = PublicClientApplication.OperatingSystemAccount;
}
try
{
// 4. Silent authentication
var authResult = await pca.AcquireTokenSilent(new[] { "User.Read" }, accountToLogin)
.ExecuteAsync();
}
// Cannot log in silently - most likely Azure AD would show a consent dialog or the user needs to re-enter credentials
catch (MsalUiRequiredException)
{
// 5. Interactive authentication
var authResult = await pca.AcquireTokenInteractive(new[] { "User.Read" })
.WithAccount(accountToLogin)
// This is mandatory so that WAM is correctly parented to your app; read on for more guidance
.WithParentActivityOrWindow(myWindowHandle)
.ExecuteAsync();
// Consider allowing the user to re-authenticate with a different account, by calling AcquireTokenInteractive again
}
ブローカーが存在しない場合 (Win 8.1、Mac、Linux など)、MSAL はブラウザーにフォールバックされ、リダイレクト URI 規則が適用されます。
リダイレクト URI
MSAL で WAM リダイレクト URI を構成する必要はありませんが、アプリの登録でそれらを構成する必要があります。
ms-appx-web://microsoft.aad.brokerplugin/{client_id}
トークン キャッシュの永続性
MSAL のトークン キャッシュを永続化することが重要です。MSAL では、ID トークンとアカウント メタデータを引き続きそこに格納するためです。 詳細については、「MSAL.NET でのトークン キャッシュのシリアル化」を参照してください。
サイレント ログインのためのアカウント
サイレント ログインのアカウントを見つけるには、次のパターンをお勧めします。
- ユーザーが既にログインしている場合は、そのアカウントを使用します。 そうでない場合は、現在の Windows アカウントについては
PublicClientApplication.OperatingSystemAccount
を使用します。 - ユーザーが対話形式でログインすることで別のアカウントに変更できるようにします。
親ウィンドウ ハンドル
WithParentActivityOrWindow
API を使用して、対話型エクスペリエンスを親にする必要のあるウィンドウで MSAL を構成する必要があります。
UI アプリケーション
Windows Forms (WinForms)、Windows Presentation Foundation (WPF)、Windows UI ライブラリ バージョン 3 (WinUI3) などの UI アプリについては、「ウィンドウ ハンドルを取得する」を参照してください。
コンソール アプリケーション
コンソール アプリケーションの場合は、ターミナル ウィンドウとそのタブのために、構成がもう少し複雑になります。 次のコードを使用します。
enum GetAncestorFlags
{
GetParent = 1,
GetRoot = 2,
/// <summary>
/// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent.
/// </summary>
GetRootOwner = 3
}
/// <summary>
/// Retrieves the handle to the ancestor of the specified window.
/// </summary>
/// <param name="hwnd">A handle to the window whose ancestor will be retrieved.
/// If this parameter is the desktop window, the function returns NULL. </param>
/// <param name="flags">The ancestor to be retrieved.</param>
/// <returns>The return value is the handle to the ancestor window.</returns>
[DllImport("user32.dll", ExactSpelling = true)]
static extern IntPtr GetAncestor(IntPtr hwnd, GetAncestorFlags flags);
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
// This is your window handle!
public IntPtr GetConsoleOrTerminalWindow()
{
IntPtr consoleHandle = GetConsoleWindow();
IntPtr handle = GetAncestor(consoleHandle, GetAncestorFlags.GetRootOwner );
return handle;
}
トラブルシューティング
"WAM アカウント ピッカーからアカウントが返されない" というエラー メッセージ
"WAM アカウント ピッカーからアカウントが返されない" というメッセージは、アカウントを表示するダイアログをアプリケーション ユーザーが閉じたこと、またはダイアログ自体がクラッシュしたことを示します。 Windows コントロールである AccountsControl
が Windows に正しく登録されていない場合に、クラッシュが発生する可能性があります。 これを解決するには、次の手順に従います。
タスク バーで、[スタート] を右クリックして、[Windows PowerShell (管理者)] を選択します。
ユーザー アカウント制御ダイアログによって入力を求められた場合は、[はい] を選択して PowerShell を起動します。
以下のスクリプトをコピーして実行します。
if (-not (Get-AppxPackage Microsoft.AccountsControl)) { Add-AppxPackage -Register "$env:windir\SystemApps\Microsoft.AccountsControl_cw5n1h2txyewy\AppxManifest.xml" -DisableDevelopmentMode -ForceApplicationShutdown } Get-AppxPackage Microsoft.AccountsControl
単一ファイルのデプロイ中に "MsalClientException: エラー コード: wam_runtime_init_failed" というエラー メッセージが表示される
アプリケーションを 1 つのファイル バンドルにパッケージ化すると、次のエラーが表示されることがあります。
MsalClientException: wam_runtime_init_failed: The type initializer for 'Microsoft.Identity.Client.NativeInterop.API' threw an exception. See https://aka.ms/msal-net-wam#troubleshooting
このエラーは、Microsoft.Identity.Client.NativeInterop のネイティブ バイナリが 1 つのファイル バンドルにパッケージ化されなかったことを示します。 抽出用にそれらのファイルを埋め込み、1 つの出力ファイルを取得するには、IncludeNativeLibrariesForSelfExtract
プロパティを true
に設定します。 詳細については、ネイティブ バイナリを 1 つのファイルにパッケージ化する方法を参照してください。
接続の問題
"接続をチェックしてからやり直してください" のようなエラー メッセージがアプリケーション ユーザーに定期的に表示される場合は、「Office のトラブルシューティング ガイド」を参照してください。 このトラブルシューティング ガイドでは、ブローカーも使用します。
サンプル
WAM を使用する WPF サンプルは、GitHub にあります。
次のステップ
このシナリオの次の記事である「デスクトップ アプリから Web API を呼び出す」に進みます。