웹 API를 호출하는 데스크톱 앱: WAM을 사용하여 토큰 획득
MSAL(Microsoft 인증 라이브러리)은 인증 브로커 역할을 하는 Windows 10+ 구성 요소인 WAM(웹 계정 관리자)을 호출합니다. 브로커를 사용하면 앱 사용자가 Windows 세션에 로그인한 계정과 같이 Windows에 알려진 계정과의 통합을 활용할 수 있습니다.
WAM 가치 제안
WAM과 같은 인증 브로커를 사용하면 많은 이점이 있습니다.
- 보안 강화. 토큰 보호를 참조하세요.
- Windows Hello, 조건부 액세스 및 FIDO 키를 지원합니다.
- Windows 이메일 및 계정 보기와 통합됩니다.
- 빠른 SSO(Single Sign-On).
- 현재 Windows 계정으로 자동 로그인하는 기능.
- Windows와 함께 제공되는 버그 수정 및 개선 사항.
WAM 제한 사항
- WAM은 Windows 10 이상 및 Windows Server 2019 이상에서 사용할 수 있습니다. Mac, Linux 및 이전 버전의 Windows에서 MSAL은 자동으로 브라우저로 대체됩니다.
- Azure AD B2C(Azure Active Directory B2C) 및 AD FS(Active Directory Federation Services) 기관은 지원되지 않습니다. 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
}
브로커가 없는 경우(예: Windows 8.1, Mac 또는 Linux) MSAL은 리디렉션 URI 규칙이 적용되는 브라우저로 대체됩니다.
리디렉션 URI
MSAL에서는 WAM 리디렉션 URI를 구성할 필요가 없지만 앱 등록에서는 구성해야 합니다.
ms-appx-web://microsoft.aad.brokerplugin/{client_id}
토큰 캐시 지속성
MSAL은 ID 토큰과 계정 메타데이터를 계속해서 여기에 저장하므로 MSAL 토큰 캐시를 유지해야 합니다. 자세한 내용은 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: ErrorCode: wam_runtime_init_failed" 오류 메시지
애플리케이션을 단일 파일 번들로 패키지할 때 다음 오류가 나타날 수 있습니다.
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의 네이티브 이진 파일이 단일 파일 번들로 패키지되지 않았음을 나타냅니다. 추출을 위해 해당 파일을 포함하고 하나의 출력 파일을 가져오려면 IncludeNativeLibrariesForSelfExtract
속성을 true
로 설정합니다. 네이티브 이진 파일을 단일 파일로 패키지하는 방법에 대해 자세히 알아봅니다.
연결 문제
애플리케이션 사용자에게 "연결을 확인하고 다시 시도하세요"와 유사한 오류 메시지가 정기적으로 표시되는 경우 Office 문제 해결 가이드를 참조하세요. 해당 문제 해결 가이드도 브로커를 사용합니다.
예제
WAM을 사용하는 WPF 샘플은 GitHub에서 찾을 수 있습니다.
다음 단계
이 시나리오의 다음 문서로 이동하여 데스크톱 앱에서 웹 API를 호출합니다.