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
相关产品推荐
相关产品推荐

