ASP Core 2.0 如何获取Azure AD B2C 返回的token?

How to obtain the token returned from Azure AD B2C in ASP Core 2.0?

我已经使用 Visual Studio 的最新 New Project 向导创建了一个 ASP Core 2.0 网页(Razor Pages),它使用 Individual Accounts 作为我的身份验证选项。我创建了一个 Azure AD B2C 租户并验证它是否正常工作。

当我 运行 向导创建的 Web 应用程序并单击右上角的 登录 时,它会重定向到我的 Azure AD B2C 站点,并且我可以正常登录。

登录后,回调 url 转到我的用户密码中配置的端点:

 ...
 "CallbackPath": "/signin-oidc",
 ...

似乎一切正常。我了解到 Azure AD B2C 门户将令牌发送回上述 /signin-oidc 回调路径并存储它。

如何检索该令牌的值?

我一直在关注所有 Azure AD B2C 指南,但并非所有指南都已更新到 ASP Core 2.0,并且 none 其中似乎使用了从15.4 VS 精灵这样:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddAzureAdB2C(options => Configuration.Bind("AzureAdB2C", options))
    .AddCookie();

    services.AddMvc();
}

注:.AddAzureAdB2C(...)

None 的 B2C 样本正在使用它,所以我很难理解。

我的最终目标是获取令牌并将其用于 API 类 的强类型集中,我使用 Autorest 从 Swagger 生成,这需要令牌。

Azure AD B2C .Net Core 示例中概述了执行此操作的最佳方法,特别是 branch for Core 2.0.

在正常的 model/flow 中,您的应用程序将获得一个 id_token 和一个授权码,但不会获得令牌。您的中间层需要将授权代码交换为令牌。这个令牌就是您随后要发送到您的网站的内容 API。

执行此操作的方法包括以下内容:

  1. 确保您的中间层正在为您的主要政策请求 id_token+ 代码(您不想为您的编辑配置文件或密码重置政策执行此操作)。来自示例的 OpenIdConnectOptionsSetup.cs#L77:
public Task OnRedirectToIdentityProvider(RedirectContext context)
{
    var defaultPolicy = AzureAdB2COptions.DefaultPolicy;
    if (context.Properties.Items.TryGetValue(AzureAdB2COptions.PolicyAuthenticationProperty, out var policy) &&
        !policy.Equals(defaultPolicy))
    {
        context.ProtocolMessage.Scope = OpenIdConnectScope.OpenIdProfile;
        context.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
        context.ProtocolMessage.IssuerAddress = context.ProtocolMessage.IssuerAddress.ToLower().Replace(defaultPolicy.ToLower(), policy.ToLower());
        context.Properties.Items.Remove(AzureAdB2COptions.PolicyAuthenticationProperty);
    }
    else if (!string.IsNullOrEmpty(AzureAdB2COptions.ApiUrl))
    {
        context.ProtocolMessage.Scope += $" offline_access {AzureAdB2COptions.ApiScopes}";
        // -----------------------------
        // THIS IS THE IMPORTANT PART:
        context.ProtocolMessage.ResponseType = OpenIdConnectResponseType.CodeIdToken;
        // -----------------------------
    }
    return Task.FromResult(0);
}
  1. 兑换代币。来自示例的 OpenIdConnectOptionsSetup.cs#L103-L124:
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;
    }
}
  1. 然后您可以在代码的其他地方使用此标记来调用 API。来自示例的 HomeController.cs#L45-L57:
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);


HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, AzureAdB2COptions.ApiUrl);


// Add token to the Authorization header and make the request
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);

ASP.NET 核心团队创建了 2 个关于此过程的优秀文档。