MSAL - PublicClientApplication - GetAccountsAsync() 不返回任何帐户

Tob*_*obi 5 c# single-sign-on azure-active-directory azure-ad-graph-api azure-ad-msal

我正在开发一个小 WPF 应用程序,该应用程序应该从 MS Graph API 查询一些数据。我想使用 SSO,这样用户就不必单独登录应用程序。

该应用程序在加入 Azure AD 的设备上运行。该用户是AADC同步的AD用户。AAD 租户与 ADFS 联合。用户使用 Hello for Business (PIN) 或密码进行身份验证。由此产生的问题是相同的。

我可以确认用户通过以下方式获得了 PRT:

dsregcmd /status

AzureAdPrt: YES
Run Code Online (Sandbox Code Playgroud)

如果重要的话:Azure AD 中的应用程序注册设置为“将应用程序视为公共客户端”。并配置以下重定向 URI:

根据我找到的示例,我使用以下代码来尝试获取访问令牌。但是该GetAccountsAsync()方法不会返回任何用户,也不会引发任何错误或异常。

谁能告诉我,我在这里缺少什么?

任何帮助将非常感激!

PS:当我使用“交互式身份验证”尝试此操作时,效果很好。

public GraphAuthProvider(string appId, string tenantId, string[] scopes)
{
    _scopes = scopes;

    try
    {
        _msalClient = PublicClientApplicationBuilder
                .Create(appId)
                .WithAuthority(AadAuthorityAudience.AzureAdMyOrg, true)
                .WithTenantId(tenantId)
                .Build();
    }
    catch (Exception exception)
    {
        _log.Error(exception.Message);
        _log.Error(exception.StackTrace);
        throw;
    }
}

public async Task<string> GetAccessToken()
{
    _log.Info("Starting 'GetAccessToken'...");
    var accounts = await _msalClient.GetAccountsAsync();
    _userAccount = accounts.FirstOrDefault();


    // If there is no saved user account, the user must sign-in
    if (_userAccount == null)
    {
        _log.Info("No cached accounts found. Trying integrated authentication...");
        [...]
    }
    else
    {
        // If there is an account, call AcquireTokenSilent
        // By doing this, MSAL will refresh the token automatically if
        // it is expired. Otherwise it returns the cached token.

        var userAccountJson = await Task.Factory.StartNew(() => JsonConvert.SerializeObject(_userAccount));
        _log.Info($"Found cached accounts. _userAccount is: {userAccountJson}");

        var result = await _msalClient
            .AcquireTokenSilent(_scopes, _userAccount)
            .ExecuteAsync();

        return result.AccessToken;
    }
}
Run Code Online (Sandbox Code Playgroud)

Tia*_*nck 7

为了能够IAccounts从 MSAL(访问缓存)返回,它必须在某个时刻引导缓存。您错过了起点,在您的情况下是AcquireTokenInteractive

建议在 MSAL 上使用以下try/catch模式:

try
{
    var accounts = await _msalClient.GetAccountsAsync();

    // Try to acquire an access token from the cache. If an interaction is required, MsalUiRequiredException will be thrown.
    result = await _msalClient.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
                .ExecuteAsync();
}
catch (MsalUiRequiredException)
{
    // Acquiring an access token interactively. MSAL will cache it so you can use AcquireTokenSilent on future calls.
    result = await _msalClient.AcquireTokenInteractive(scopes)
                .ExecuteAsync();
}

Run Code Online (Sandbox Code Playgroud)

使用这个try/catch模式而不是if/else逻辑,你就可以开始了。

为了进一步参考,有这个msal 桌面示例,其中涵盖了许多常见场景。

更新

如果您在每个操作上实例化一个新的 _msalClient,那么这就解释了为什么其他调用不起作用。您可以将 _msalClient 作为静态/单例实例,也可以实现序列化令牌缓存。这是一个缓存示例

  • 实际上,我希望我可以在用户部分完全不进行任何交互的情况下对用户进行身份验证。由于用户位于加入 Azure AD 的计算机上,因此这应该是可能的。例如,当我浏览 Portal.office.com 时,不需要任何身份验证。该文档:https://learn.microsoft.com/en-us/azure/active-directory/devices/concept-primary-refresh-token#prt-usage-during-app-token-requests 特别是此图给出了印象中,这应该是可能的 (2认同)