ASP.NET Core中ClaimActions.MapJsonKey无法覆盖声明的问题
解决方案
问题根源
- 默认Claim映射覆盖自定义设置:
AddMicrosoftAccount默认内置了ClaimTypes.Name和ClaimTypes.Email的映射规则,会优先使用Graph API返回的name(姓名)和userPrincipalName(可能带#EXT#的租户格式邮箱),导致你的自定义MapJsonKey不生效。 - 用户信息来源优先级:默认认证流程会调用Microsoft Graph的
/me接口获取用户信息,而非直接解析AccessToken的JWT声明。
修复步骤
1. 清除默认Claim映射
先移除默认的Name和Email映射规则,避免覆盖自定义配置:
msOptions.ClaimActions.Remove(ClaimTypes.Name); msOptions.ClaimActions.Remove(ClaimTypes.Email); // 若需要完全重置所有默认映射,可使用:msOptions.ClaimActions.Clear();
2. 添加必要的权限范围
确保请求的权限包含获取邮箱所需的email或User.Read(后者会返回完整用户信息,包括mail字段):
msOptions.Scope.Add("User.Read"); msOptions.Scope.Add("email");
3. 配置自定义Claim映射
根据你需要的数据源选择对应配置:
方式一:从Graph API返回的用户信息中映射(推荐)
Graph API的/me接口返回的mail字段是用户的实际邮箱(无#EXT#),直接映射到对应的Claim类型:
msOptions.ClaimActions.MapJsonKey(ClaimTypes.Name, "mail"); msOptions.ClaimActions.MapJsonKey(ClaimTypes.Email, "mail");
方式二:直接从AccessToken的JWT声明中映射
如果不想调用Graph API,可关闭用户信息获取,直接使用AccessToken中的email声明:
// 关闭自动调用Graph API获取用户信息 msOptions.FetchUserDetails = false; // 映射AccessToken中的email声明 msOptions.ClaimActions.MapJsonKey(ClaimTypes.Name, "email"); msOptions.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
4. 简化OnCreatingTicket事件(可选)
如果选择方式一,可完全移除手动修改Claim的冗余代码;若仍需自定义处理,可简化为:
msOptions.Events = new OAuthEvents { OnCreatingTicket = context => { var email = context.User.GetString("mail"); context.Identity.AddClaim(new Claim(ClaimTypes.Name, email)); return Task.CompletedTask; } };
修改后的完整代码示例
builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = MicrosoftAccountDefaults.AuthenticationScheme; }).AddCookie(options => { options.LoginPath = "/signin-microsoft"; }) .AddMicrosoftAccount(msOptions => { msOptions.ClientId = builder.Configuration.GetValue<string>("MicrosoftAuthConfig:ClientId"); msOptions.ClientSecret = builder.Configuration.GetValue<string>("MicrosoftAuthConfig:ClientSecret"); msOptions.AuthorizationEndpoint = builder.Configuration.GetValue<string>("MicrosoftAuthConfig:AuthorizationEndpoint"); msOptions.TokenEndpoint = builder.Configuration.GetValue<string>("MicrosoftAuthConfig:TokenEndpoint"); // 清除默认的Name和Email映射 msOptions.ClaimActions.Remove(ClaimTypes.Name); msOptions.ClaimActions.Remove(ClaimTypes.Email); // 添加必要的权限范围 msOptions.Scope.Add("User.Read"); msOptions.Scope.Add("email"); // 自定义Claim映射(从Graph API的mail字段获取) msOptions.ClaimActions.MapJsonKey(ClaimTypes.Name, "mail"); msOptions.ClaimActions.MapJsonKey(ClaimTypes.Email, "mail"); // 保留需要的事件处理 msOptions.Events = new OAuthEvents { OnRedirectToAuthorizationEndpoint = context => { Console.WriteLine($"Redirecting to: {context.RedirectUri}"); context.Response.Redirect(context.RedirectUri); return Task.CompletedTask; } }; });
关键说明
- 若用户是外部账户(如个人微软账户登录企业租户),
userPrincipalName会包含#EXT#,而mail字段才是用户的真实邮箱,因此优先映射mail字段。 - 确保Azure AD应用注册中已配置正确的权限(
User.Read和email),并授予管理员同意(若为租户应用)。
内容的提问来源于stack exchange,提问作者Legion
相关产品推荐
相关产品推荐

