【问题标题】:How to obtain the token returned from Azure AD B2C in ASP Core 2.0?如何在 ASP Core 2.0 中获取 Azure AD B2C 返回的令牌?
【发布时间】:2017-10-24 00:07:16
【问题描述】:

我使用 Visual Studio 最新的新建项目向导创建了一个使用个人帐户作为我的身份验证选项的 ASP Core 2.0 网页(Razor 页面)。我创建了一个 Azure AD B2C 租户并验证它可以正常工作。

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

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

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

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

如何检索该令牌的值?

我一直在关注所有 Azure AD B2C 指南,但并非所有指南都已更新到 ASP Core 2.0,而且它们似乎都没有使用从 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(...)

没有一个 B2C 示例使用这个,所以我很难理解。

我的最终目标是获取令牌并在我使用 Autorest 从 Swagger 生成的一组强类型 API 类中使用它,这需要令牌。

【问题讨论】:

    标签: azure-ad-b2c


    【解决方案1】:

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

    在正常的模型/流程中,您的应用程序将获得一个 id_token 和一个授权码,但不会获得一个令牌。您的中间层需要将授权码换成令牌。该令牌是您随后将发送到您的 Web API 的内容。

    这样做的方法涉及以下内容:

    1. 确保您的中间层为您的主要策略请求 id_token+code(您不想为您的编辑配置文件或密码重置策略执行此操作)。来自样本的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);
    

    【讨论】:

    • 很受启发,不知道2.0的文档刷新在分支下。您的代码答案也正是我所需要的。
    • 这太冗长了。我不敢相信他们没有像现在 IdentityServer 那样精简它。我应该能够在 AddAzureADB2C IServiceCollection 方法中指定范围,然后它应该自动退出并尝试为我获取访问令牌,而无需我自己推出 IConfigureNamedOptions
    【解决方案2】:

    ASP.NET Core 团队在这个过程中创建了 2 个优秀的文档。

    【讨论】:

    • 这些链接非常有用。感谢您跟进最新文件。文档继续给我留下深刻印象!
    • @Kbalz 我是这些作品的作者——我很高兴你喜欢它们!如果您还有其他需要,请告诉我。
    猜你喜欢
    • 2018-10-15
    • 1970-01-01
    • 2018-01-21
    • 1970-01-01
    • 2018-09-03
    • 2021-10-26
    • 2021-03-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多