无法使用 asp.net 核心中的 MSAL 1.1.0-preview 通过授权代码获取 AccessToken

Failed to get AccessToken via authorization code using MSAL 1.1.0-preview in asp.net core

我按照下面的官方步骤尝试了场景“web app calling a Web API in Azure Ad B2C”,唯一的区别是我使用的是Asp.Net核心。我正在使用 AuthorizationCode 获取访问令牌,但它总是 returns 带有 id 令牌和 NULL 访问令牌.

我的代码:

app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
    AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme,
    AutomaticChallenge = true,
    ClientId = aadB2cSettings.ClientId,
    MetadataAddress = $"{aadB2cSettings.Instance}{aadB2cSettings.Tenant}/v2.0/.well-known/openid-configuration?p={aadB2cSettings.B2cSignUpOrSignInPolicy}",
    PostLogoutRedirectUri = aadB2cSettings.RedirectUrl,

    ResponseType = OpenIdConnectResponseType.CodeIdToken,
    TokenValidationParameters = new TokenValidationParameters
    {
        NameClaimType = "name"
    },

    Events = new OpenIdConnectEvents
    {
        OnAuthorizationCodeReceived = async context =>
        {
            var authCode = context.TokenEndpointRequest.Code;
            var b2cAuthority = $"{aadB2cSettings.Instance}tfp/{aadB2cSettings.Tenant}/{aadB2cSettings.B2cSignUpOrSignInPolicy}/v2.0/.well-known/openid-configuration";
            var cca = new ConfidentialClientApplication(
                aadB2cSettings.ClientId, 
                b2cAuthority,
                aadB2cSettings.RedirectUrl, 
                new ClientCredential(aadB2cSettings.ClientSecret), 
                new TokenCache(), 
                null);

            try
            {
                var authResult = await cca.AcquireTokenByAuthorizationCodeAsync(authCode, new[] { "https://hulab2c.onmicrosoft.com/b2cdemo/all" });
                context.HandleCodeRedemption(authResult.AccessToken, authResult.IdToken);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    },

使用fiddler抓取请求,是:

POST https://login.microsoftonline.com/hulab2c.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_signuporsignin HTTP/1.1

请求正文:

client_id=1ff91f47-08ee-4973-83f4-379ad7e0679c&client_info=1&client_secret=......&scope=https%3A%2F%2Fhulab2c.onmicrosoft.com%2Fb2cdemo%2Fall+offline_access+openid+profile&grant_type=authorization_code&code=......&redirect_uri=https%3A%2F%2Flocalhost%3A44383%2F

Return:

{"id_token":"......","token_type":"Bearer","not_before":1494494423,"client_info":"......","scope":""}

所以只有 id 令牌,没有访问令牌。但是我们应该在这里获取访问令牌,对吗?

https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi/issues/4 Microsoft 已重现该问题并正在修复

终于找到我失败的原因:获取AuthorizationCode的请求不包含目标范围。反映在代码中,对于aspnetcore中的OpenIdConnectOption,Scope参数是只读的,默认值为"opened profile"。 Scope is readonly in OpenIdConnectOption

所以默认发送的授权码请求是:

GET https://login.microsoftonline.com/hulab2c.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_signuporsignin&client_id=7f865ca0-271e-4f27-be21-6f0072fe3ad7&redirect_uri=https%3A%2F%2Flocalhost%3A44355%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile&response_mode=form_post&nonce=...... HTTP/1.1

因此,使用此授权码响应获取令牌,即使我们在令牌请求中设置了正确的范围,我们仍然无法获取访问码,而只能获取 ID 令牌,因为提供的授权码仅用于 "openid profile".

要解决此问题,我们还需要将目标网络 api 范围添加到授权代码中。这是如何修复代码:

Events = new OpenIdConnectEvents
{
       OnRedirectToIdentityProvider = context =>
       {
            context.ProtocolMessage.Scope += $" offline_access {myapiscope}";

            return Task.FromResult(0);
       },
       ......
}

在AspNet中,我们不需要这样做,因为它的作用域不像aspnetcore那样只读,可以直接设置:

new OpenIdConnectAuthenticationOptions
{
      ......
      Scope = $"openid profile offline_access {ReadTasksScope} {WriteTasksScope}"
}