ASP.NET中已认证用户Claims更新:谷歌注册补全同步问题
基于ASP.NET Core + OpenIdDict实现的身份服务器(IDP),支持自有用户密码登录和谷歌认证代理。用户通过谷歌注册后会被重定向到IDP补全信息,补全后IDP会标记用户为已注册(更新isUserRegistered Claim),但该更新仅在IDP生效,客户端和API无法同步获取到最新的Claim。尝试过移除/添加Claim后调用RefreshSignInAsync,甚至登出再登录,都无法解决同步问题,需要无缝的解决方案。
尝试的代码
var user = await _userManager.GetUserAsync(User); var claims = await _userManager.GetClaimsAsync(user); var isUserRegistered = claims.FirstOrDefault(c => c.Type == AuthConstants.Claims.isUserRegistered ); if (isUserRegistered != null) { await _userManager.RemoveClaimAsync(user, isUserRegistered ); } isUserRegistered = new Claim(AuthConstants.Claims.isUserRegistered , true.ToString()); await _userManager.AddClaimAsync(user, isUserRegistered); await _signInManager.RefreshSignInAsync(user);
解决方案
核心原因
RefreshSignInAsync仅更新IDP本地的用户会话Cookie,而客户端和API持有的是之前颁发的OAuth2/OIDC令牌(访问令牌、ID令牌),这些令牌在颁发时就已固化当时的Claim信息,不会自动同步IDP后续的Claim更新。
无缝解决方法
触发客户端刷新令牌
在用户补全信息并完成IDP端的Claim更新后,引导客户端发起刷新令牌请求(使用之前获取的刷新令牌)。IDP在处理刷新请求时,会重新从UserManager获取用户最新的Claim信息,生成包含更新后isUserRegistered的新访问令牌和ID令牌,客户端替换旧令牌后,API就能获取到最新Claim。确保IDP刷新令牌时重新加载用户Claims
检查OpenIdDict的配置,确保在处理刷新令牌请求时,不会使用令牌中的旧Claim缓存,而是每次都从用户数据源获取最新Claims。可通过自定义UserClaimsPrincipalFactory实现:// 自定义ClaimsPrincipal工厂,确保加载最新的用户Claim public class CustomUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser> { public CustomUserClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, optionsAccessor) { } protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user) { var identity = await base.GenerateClaimsAsync(user); // 手动更新isUserRegistered Claim,确保为最新值 var isRegisteredClaim = await UserManager.GetClaimAsync(user, AuthConstants.Claims.isUserRegistered); if (isRegisteredClaim != null) { var existingClaim = identity.FindFirst(AuthConstants.Claims.isUserRegistered); if (existingClaim != null) identity.RemoveClaim(existingClaim); identity.AddClaim(isRegisteredClaim); } return identity; } }在Startup中注册该工厂:
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, CustomUserClaimsPrincipalFactory>();直接颁发新令牌(自定义场景可选)
在用户补全信息的端点中,直接调用OpenIdDict的令牌服务生成新的访问令牌和ID令牌,返回给客户端替换旧令牌。这种方式需要配合客户端的令牌存储逻辑调整,适合定制化程度较高的客户端场景。
参考提示
遇到同类问题可参考《ASP.NET Core Identity中使用外部身份提供者》一文的内容。
内容的提问来源于stack exchange,提问作者Adam.P

