ID令牌缺少特定于Azure AD v2.0的可选声明

时间:2020-07-10 10:46:59

标签: azure-active-directory asp.net-core-3.1 claims-authentication

我正在尝试使用Microsoft Identity Web-NuGet添加可选声明,以在NET Core 3.1 WebApp中进行用户身份验证。阅读MS Docs,似乎所需的唯一步骤是在Azure的App Registration Manifest文件中声明可选声明。但是,当使用两个不同的应用程序(我自己的代码和一个MS项目示例)测试登录过程时,成功登录后从Azure返回时似乎没有将可选声明添加到ID令牌中,即它们根本不存在在调试中查看令牌详细信息时。

我不确定如何诊断该问题以及在何处跟踪该问题,即我是否缺少Azure安装程序中的所有必需步骤?

侧面注意:只是要确认这是我要接收其他声明的jwt ID令牌,而不是用于调用图形或另一个Web API端点的jwt访问令牌。

MS Docs参考:v2.0-specific optional claims set

以下是清单文件的摘录:(请注意,我什至已经声明了“ accessTokenAcceptedVersion”:2,因为我正在使用的可选声明在版本1中不可用,如果上述内容保留为默认设置, 'null'值,然后Azure将假定我们正在使用旧版Ver.1-可能的陷阱

"accessTokenAcceptedVersion": 2,
"optionalClaims": {
    "idToken": [
        {
            "name": "given_name",
            "source": "user",
            "essential": false,
            "additionalProperties": []
        },
        {
            "name": "family_name",
            "source": "user",
            "essential": false,
            "additionalProperties": []
        }
    ],
    "accessToken": [],
    "saml2Token": []
},

从启动类中提取:

public void ConfigureServices(IServiceCollection services)
    {
        // Added to original .net core template.
        // ASP.NET Core apps access the HttpContext through the IHttpContextAccessor interface and 
        // its default implementation HttpContextAccessor. It's only necessary to use IHttpContextAccessor 
        // when you need access to the HttpContext inside a service.
        // Example usage - we're using this to retrieve the details of the currrently logged in user in page model actions.
        services.AddHttpContextAccessor();

        // DO NOT DELETE (for now...)
        // This 'Microsoft.AspNetCore.Authentication.AzureAD.UI' library was originally used for Azure Ad authentication 
        // before we implemented the newer Microsoft.Identity.Web and Microsoft.Identity.Web.UI NuGet packages. 
        // Note after implememting the newer library for authetication, we had to modify the _LoginPartial.cshtml file.
        //services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
        //    .AddAzureAD(options => Configuration.Bind("AzureAd", options));

        ///////////////////////////////////

        // Add services required for using options.
        // e.g used for calling Graph Api from WebOptions class, from config file.
        services.AddOptions();

        // Add service for MS Graph API Service Client.
        services.AddTransient<OidcConnectEvents>();

        // Sign-in users with the Microsoft identity platform
        services.AddSignIn(Configuration);

        // Token acquisition service based on MSAL.NET
        // and chosen token cache implementation
        services.AddWebAppCallsProtectedWebApi(Configuration, new string[] { Constants.ScopeUserRead })
            .AddInMemoryTokenCaches();

        // Add the MS Graph SDK Client as a service for Dependancy Injection.
        services.AddGraphService(Configuration);

        ///////////////////////////////////

        // The following lines code instruct the asp.net core middleware to use the data in the "roles" claim in the Authorize attribute and User.IsInrole()
        // See https://docs.microsoft.com/aspnet/core/security/authorization/roles?view=aspnetcore-2.2 for more info.
        services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
        {
            // The claim in the Jwt token where App roles are available.
            options.TokenValidationParameters.RoleClaimType = "roles";
        });

        // Adding authorization policies that enforce authorization using Azure AD roles. Polices defined in seperate classes.
        services.AddAuthorization(options =>
        {
            options.AddPolicy(AuthorizationPolicies.AssignmentToViewLogsRoleRequired, policy => policy.RequireRole(AppRole.ViewLogs));
        });

        ///////////////////////////////////

        services.AddRazorPages().AddMvcOptions(options =>
        {
            var policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
            options.Filters.Add(new AuthorizeFilter(policy));
        }).AddMicrosoftIdentityUI();

        // Adds the service for creating the Jwt Token used for calling microservices.
        // Note we are using our independant bearer token issuer service here, NOT Azure AD
        services.AddScoped<JwtService>(); 
    }

Razor PageModel样本方法:

public void OnGet()
    {
        var username = HttpContext.User.Identity.Name;
        var forename = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "given_name")?.Value;
        var surname = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "family_name")?.Value;

        _logger.LogInformation("" + username + " requested the Index page");
    }

更新

更接近解决方案,但还不完全解决。解决了几个问题:

  1. 我最初在Azure中创建租户以使用B2C AD,即使我不再使用B2C并已切换到Azure AD。直到我删除租户并创建了一个新租户,才开始看到可选的声明正确地传递到Web应用程序。创建新的承租人并分配承租人类型以使用Azure AD之后,我随后发现“令牌配置”菜单现在可用于通过UI配置可选声明,看来仍然仍然需要修改App清单,如上所示。

enter image description here

  1. 我必须将“配置文件”范围添加为“代理”类型,并将其添加到Azure中的webapp API权限中。

enter image description here

最后一个尚未解决的问题是,尽管我可以看到Debug期间存在的声明,但是我无法弄清楚如何检索声明值。

在下面的方法中,使用Debug时可以看到所需的声明,但无法弄清楚如何检索值:

public void OnGet()
    {
        var username = HttpContext.User.Identity.Name;

        var forename = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "given_name")?.Value;
        var surname = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "family_name")?.Value;

        _logger.LogInformation("" + username + " requested the Index page");
    }

Debug屏幕截图显示了给定名称和家庭名称存在:

enter image description here

enter image description here

我尝试使用Claims主体尝试不同的代码示例,以尝试获取值,但对我来说没有任何用。对于知道所需语法的人来说,希望这个最后的谜语是相当简单的,正如我们现在所说的,现在存在所需的可选声明,只是不知道如何实际获取值。

1 个答案:

答案 0 :(得分:0)

非常感谢“ Dhivya G-MSFT身份”的帮助(请参见我的原始问题下方的注释),下面的方法现在使我能够从成功登录后从Azure返回的令牌ID中访问所需的索赔值。

    public void OnGet()
    {
        var username = HttpContext.User.Identity.Name;

        var forename = HttpContext.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value;
        var surname = HttpContext.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value;

        _logger.LogInformation("" + username + " requested the Index page");
    }