You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ABP应用通过OpenIdConnect从Microsoft获取用户姓名失败排查

问题:ABP应用集成Microsoft登录后无法获取用户姓名声明

我有一个Asp Boilerplate(ABP)应用,已配置OpenIdConnect实现Microsoft账号登录,本地运行时可成功登录,但调试时无法查看用户的名和姓。

以下是我的身份验证配置代码:

private static void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
    context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);

    context.Services.AddAuthentication().AddMicrosoftIdentityWebApp(configuration.GetSection("AzureAd"), cookieScheme: null);

    context.Services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
    {
        options.Authority = "https://login.microsoftonline.com/" + configuration["AzureAd:TenantId"] + "/v2.0/";
        options.ClientId = configuration["AzureAd:ClientId"];
        options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
        options.CallbackPath = configuration["AzureAd:CallbackPath"];
        options.ClientSecret = configuration["AzureAd:ClientSecret"];
        options.RequireHttpsMetadata = false;
        options.SaveTokens = false;
        options.GetClaimsFromUserInfoEndpoint = true;
        options.SignInScheme = IdentityConstants.ExternalScheme;
        options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
        options.Scope.Add("user.read");
        options.Scope.Add("openid");
        options.Scope.Add("profile");

        // Map first and last name claims
        options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name");
        options.ClaimActions.MapJsonKey(ClaimTypes.Surname, "family_name");
    });
}

我期望调试时能在ClaimsPrincipal中看到用户的名和姓,但未获取到对应声明。

应用注册的已配置权限:
应用注册权限配置截图

令牌配置:
令牌配置截图

请问我遗漏了什么配置?


解决方案

1. 避免配置被AddMicrosoftIdentityWebApp覆盖

AddMicrosoftIdentityWebApp方法内部会自动初始化OpenIdConnectOptions,后续单独调用Configure<OpenIdConnectOptions>的配置可能被覆盖。建议将所有配置逻辑合并到AddMicrosoftIdentityWebApp的委托中:

private static void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
    context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);

    context.Services.AddAuthentication()
        .AddMicrosoftIdentityWebApp(configuration.GetSection("AzureAd"), cookieScheme: null, options =>
        {
            options.Authority = "https://login.microsoftonline.com/" + configuration["AzureAd:TenantId"] + "/v2.0/";
            options.ClientId = configuration["AzureAd:ClientId"];
            options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
            options.CallbackPath = configuration["AzureAd:CallbackPath"];
            options.ClientSecret = configuration["AzureAd:ClientSecret"];
            options.RequireHttpsMetadata = false;
            options.SaveTokens = false;
            options.GetClaimsFromUserInfoEndpoint = true;
            options.SignInScheme = IdentityConstants.ExternalScheme;
            options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
            options.Scope.Add("user.read");
            options.Scope.Add("openid");
            options.Scope.Add("profile");

            // 确保姓名声明映射生效
            options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name");
            options.ClaimActions.MapJsonKey(ClaimTypes.Surname, "family_name");
        });
}

2. 关闭默认声明筛选

Microsoft Identity和ABP可能默认过滤部分声明,添加以下配置确保目标声明被保留:

// 在OpenIdConnectOptions配置中添加
options.ClaimActions.MapAllExcept("iss", "nbf", "exp", "aud", "nonce", "iat", "c_hash");

这会映射除指定保留声明外的所有字段,避免given_name和family_name被过滤。

3. 验证ID Token是否包含目标字段

使用JWT解析工具查看返回的ID Token,确认其中是否存在given_name和family_name字段:

  • 如果令牌中没有这些字段,检查Azure AD应用注册的令牌配置,确保profile范围对应的姓名声明已勾选;
  • 同时确认登录用户的Microsoft账号已填写名和姓信息(部分测试账号可能未完善资料)。

4. 确保ABP外部登录时映射声明

在ABP的外部登录处理逻辑中,手动提取并映射外部声明到用户实体。可以重写AccountAppService中的ExternalLoginAsync方法:

public override async Task<ExternalLoginResponseDto> ExternalLoginAsync(ExternalLoginDto input)
{
    var result = await SignInManager.ExternalLoginSignInAsync(input.Provider, input.ProviderKey, input.RememberMe, bypassTwoFactor: true);
    if (result.Succeeded)
    {
        var user = await UserManager.FindByLoginAsync(input.Provider, input.ProviderKey);
        // 从ClaimsPrincipal提取姓名
        var principal = await SignInManager.CreateUserPrincipalAsync(user);
        var givenName = principal.FindFirstValue(ClaimTypes.GivenName);
        var surname = principal.FindFirstValue(ClaimTypes.Surname);
        // 保存到用户实体(如果需要)
        if (!string.IsNullOrEmpty(givenName) && user.Name != givenName)
        {
            user.Name = givenName;
            await UserManager.UpdateAsync(user);
        }
        // ... 其他逻辑
    }
    // ... 其他逻辑
}

内容的提问来源于stack exchange,提问作者user20204585

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 16:04:53