在与Azure AD B2C集成的网站中获取访问令牌

时间:2019-01-23 20:38:58

标签: asp.net-mvc azure asp.net-core azure-ad-b2c

我有一个ASP.NET MVC Core 2.2应用程序,该应用程序与Azure AD B2C集成在一起以对用户进行身份验证。我可以正确登录,并且用户已通过身份验证。

我还创建了一个ASP.NET Core Web API,该API也与Azure B2C AD集成在一起,目标是从ASP.NET MVC控制器操作方法调用该Web api。

因此,我在MVC站点的控制器中添加了以下测试代码:

if (HttpContext.User.Identity.IsAuthenticated)
{
    string signedInUserID = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
    TokenCache userTokenCache = new MSALSessionCache(signedInUserID, HttpContext).GetMsalCacheInstance();
    ConfidentialClientApplication cca = new ConfidentialClientApplication(mgpPortalApplicationId, authority, redirectUri, new ClientCredential(mgpPortalSecretKey), userTokenCache, null);                
    IEnumerable<IAccount> accounts = await cca.GetAccountsAsync();
    IAccount firstAccount = accounts.FirstOrDefault();
    AuthenticationResult result = await cca.AcquireTokenSilentAsync(null, firstAccount, authority, false);
    HttpClient client = new HttpClient();
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44307/api/values");
    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
    HttpResponseMessage response = await client.SendAsync(request);
}

问题是accounts.FirstOrDefault()返回null。不知道为什么:

有人对我在做什么错有想法吗?感谢您的提示!

其他观察结果:如果我运行使用旧版Microsoft.Identity.Client的演示https://github.com/Azure-Samples/active-directory-b2c-dotnetcore-webapp,则对cca.Users.FirstOrDefault()的调用会正确返回用户。但是,当我将此演示项目升级到Microsoft.Identity.Client 2.7(.NET Core 2.2所需)时,我必须传递一个IAccount,因此我需要调用GetAccountsAsync(),这将返回没有帐户。

3 个答案:

答案 0 :(得分:0)

首先,您需要在调用AcquireTokenSilentAsync时要求访问api所需的作用域

AuthenticationResult result = await cca.AcquireTokenSilentAsync(scope, cca.Users.FirstOrDefault(), AzureAdB2COptions.Authority, false);

您需要实现初始令牌获取,并使用授权中间件的AuthorizationCodeReceived通知将令牌保存在MSAL令牌缓存中:

public async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context)
{
    // Use MSAL to swap the code for an access token
    // Extract the code from the response notification
    var code = context.ProtocolMessage.Code;

    string signedInUserID = context.Principal.FindFirst(ClaimTypes.NameIdentifier).Value;
    TokenCache userTokenCache = new MSALSessionCache(signedInUserID, context.HttpContext).GetMsalCacheInstance();
    ConfidentialClientApplication cca = new ConfidentialClientApplication(AzureAdB2COptions.ClientId, AzureAdB2COptions.Authority, AzureAdB2COptions.RedirectUri, new ClientCredential(AzureAdB2COptions.ClientSecret), userTokenCache, null);
    try
    {
        AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(code, AzureAdB2COptions.ApiScopes.Split(' '));


        context.HandleCodeRedemption(result.AccessToken, result.IdToken);
    }
    catch (Exception ex)
    {
        //TODO: Handle
        throw;
    }
}

因此,当在控制器中获取令牌时,MSAL将查找缓存并返回与要求匹配的所有缓存令牌。如果此类访问令牌已过期或没有合适的访问令牌,但存在关联的刷新令牌,则MSAL将自动使用该令牌来获取新的访问令牌并透明地返回:

// Retrieve the token with the specified scopes
var scope = AzureAdB2COptions.ApiScopes.Split(' ');
string signedInUserID = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
TokenCache userTokenCache = new MSALSessionCache(signedInUserID, this.HttpContext).GetMsalCacheInstance();
ConfidentialClientApplication cca = new ConfidentialClientApplication(AzureAdB2COptions.ClientId, AzureAdB2COptions.Authority, AzureAdB2COptions.RedirectUri, new ClientCredential(AzureAdB2COptions.ClientSecret), userTokenCache, null);

AuthenticationResult result = await cca.AcquireTokenSilentAsync(scope, cca.Users.FirstOrDefault(), AzureAdB2COptions.Authority, false);

Here是使用ASP.NET Core 2.0和MSAL 2的代码示例,请阅读README.md以获得详细说明。

您还可以单击here以获得代码示例,该代码示例针对Azure AD B2C对用户进行身份验证,并使用ASP.NET Core 2.1使用MSAL.NET获取访问令牌。

答案 1 :(得分:0)

@ L-Four,实际上您提到的代码的一个分支已经更新为最新版本的MSAL: active-directory-b2c-dotnetcore-webapp

说,在运行它时,出现未授权的错误。因此,我不太确定这是怎么回事。但是我确实找回了一个帐户。

答案 2 :(得分:0)

.NETCore B2C web app已更新为使用MSAL v2.7。使用的范围不正确,因此示例现在使用了正确的范围,并返回了访问令牌。

"ApiScopes": "https://fabrikamb2c.onmicrosoft.com/helloapi/demo.read"