IdentityServer4对接Wordpress时UserInfo端点无法返回完整用户信息
IdentityServer4对接Wordpress仅获取sub字段的问题排查与解决
我现有一套基于.NET Core的IdentityServer4认证系统,已有多个.NET应用成功对接。现在需要将该系统与Wordpress应用对接,已安装miniOrange OAuth/OpenID Connect单点登录插件,连接功能正常,但在插件测试界面仅能获取到sub字段,无法显示邮箱、姓名等其他用户信息。
IdentityServer4的Program.cs代码
builder.Services.AddRazorPages(); builder.Services.Configure<CookiePolicyOptions>(options => { options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); builder.Services.AddIdentityServer() .AddConfigurationStore(options => { options.ConfigureDbContext = optionsBuilder => optionsBuilder.UseSqlServer(configuration.GetConnectionString("BddConnectContext"), sql => sql.MigrationsAssembly(typeof(Program).GetTypeInfo().Assembly.GetName().Name)); }) .AddDeveloperSigningCredential() .AddProfileService<ProfileService>(); builder.Services.AddTransient<IUtilisateurRepository, UtilisateurRepository>(); builder.Services.AddDbContext<BddContext>(options => options.UseSqlServer(configuration.GetConnectionString("BddContext"))); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseIdentityServer(); app.MapRazorPages().RequireAuthorization(); app.UseEndpoints(option => { option.MapControllerRoute( name: "default", pattern: "{controller=Login}/{action=Index}/{id?}" ); }); app.Run();
WordPress客户端配置截图

IdentityServer4客户端配置
{ "AbsoluteRefreshTokenLifetime": 2592000, "AccessTokenLifetime": 3600, "AccessTokenType": 0, "AllowAccessTokensViaBrowser": false, "AllowOfflineAccess": true, "AllowPlainTextPkce": false, "AllowRememberConsent": true, "AllowedCorsOrigins": [], "AllowedGrantTypes": [ "implicit" ], "AllowedIdentityTokenSigningAlgorithms": [], "AllowedScopes": [ "email", "openid", "profile" ], "AlwaysIncludeUserClaimsInIdToken": true, "AlwaysSendClientClaims": false, "AuthorizationCodeLifetime": 300, "BackChannelLogoutSessionRequired": true, "BackChannelLogoutUri": null, "CibaLifetime": null, "Claims": [], "ClientClaimsPrefix": "client_", "ClientId": "wordpress", "ClientName": "My WordPress", "ClientSecrets": [], "ClientUri": null, "ConsentLifetime": null, "CoordinateLifetimeWithUserSession": false, "Description": null, "DeviceCodeLifetime": 300, "EnableLocalLogin": true, "Enabled": true, "FrontChannelLogoutSessionRequired": true, "FrontChannelLogoutUri": null, "IdentityProviderRestrictions": [], "IdentityTokenLifetime": 300, "IncludeJwtId": false, "LogoUri": null, "PairWiseSubjectSalt": null, "PollingInterval": 0, "PostLogoutRedirectUris": [ "https://sandbox.miniorange.com/signout-callback-oidc" ], "Properties": {}, "ProtocolType": "oidc", "RedirectUris": [ "https://sandbox.miniorange.com/314cfc5" ], "RefreshTokenExpiration": 1, "RefreshTokenUsage": 1, "RequireClientSecret": true, "RequireConsent": false, "RequirePkce": false, "RequireRequestObject": false, "SlidingRefreshTokenLifetime": 1296000, "UpdateAccessTokenClaimsOnRefresh": false, "UserCodeType": null, "UserSsoLifetime": null }
IdentityServer4元数据配置
{ "authorization_endpoint": "https://my-server.com/connect/authorize", "authorization_response_iss_parameter_supported": true, "backchannel_authentication_endpoint": "https://my-server.com/connect/ciba", "backchannel_logout_session_supported": true, "backchannel_logout_supported": true, "backchannel_token_delivery_modes_supported": [ "poll" ], "backchannel_user_code_parameter_supported": true, "check_session_iframe": "https://my-server.com/connect/checksession", "claims_supported": [ "sub", "name", "email", "birthdate", "family_name", "gender", "given_name", "locale", "middle_name", "nickname", "picture", "preferred_username", "profile", "updated_at", "website", "zoneinfo" ], "code_challenge_methods_supported": [ "plain", "S256" ], "device_authorization_endpoint": "https://my-server.com/connect/deviceauthorization", "end_session_endpoint": "https://my-server.com/connect/endsession", "frontchannel_logout_session_supported": true, "frontchannel_logout_supported": true, "grant_types_supported": [ "authorization_code", "client_credentials", "refresh_token", "implicit", "urn:ietf:params:oauth:grant-type:device_code", "urn:openid:params:grant-type:ciba" ], "id_token_signing_alg_values_supported": [ "RS256" ], "introspection_endpoint": "https://my-server.com/connect/introspect", "issuer": "https://my-server.com", "jwks_uri": "https://my-server.com/.well-known/openid-configuration/jwks", "request_object_signing_alg_values_supported": [ "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512", "HS256", "HS384", "HS512" ], "request_parameter_supported": true, "response_modes_supported": [ "form_post", "query", "fragment" ], "response_types_supported": [ "code", "token", "id_token", "id_token token", "code id_token", "code token", "code id_token token" ], "revocation_endpoint": "https://my-server.com/connect/revocation", "scopes_supported": [ "openid", "profile", "email", "offline_access" ], "subject_types_supported": [ "public" ], "token_endpoint": "https://my-server.com/connect/token", "token_endpoint_auth_methods_supported": [ "client_secret_basic", "client_secret_post" ], "userinfo_endpoint": "https://my-server.com/connect/userinfo" }
ProfileService代码
public class ProfileService : IProfileService { private readonly IUserCustomRepository _userRepository; public ProfileService(IUserCustomRepository userRepository) { _userRepository = userRepository; } public Task GetProfileDataAsync(ProfileDataRequestContext context) { var idUserCustom = context.Subject.FindFirst(x => x.Type == "IdUserCustom"); if (idUserCustom != null) { var utilisateur = _userRepository.GetUserCustomById(int.Parse(idUserCustom.Value)); if (utilisateur != null) { var listAppli = _userRepository.ListApplicationUser(utilisateur).OrderBy(x => x.RangAffichage).ToList(); context.IssuedClaims = GetClaims(utilisateur, listAppli); } } return Task.FromResult(0); } public Task IsActiveAsync(IsActiveContext context) { return Task.FromResult(0); } private List<Claim> GetClaims(UserCustom customUser, List<Application> listAppli) { var result = new List<Claim> { new(JwtClaimTypes.Name, customUser.PrenomUserCustom ?? ""), new(JwtClaimTypes.FamilyName, customUser.NomUserCustom ?? ""), new(JwtClaimTypes.Email, customUser.EmailUserCustom ?? ""), new("Login", customUser.LoginUserCustom), new("UserCustom", JsonConvert.SerializeObject(customUser)) }; if (listAppli.Count > 0) { foreach (var appli in listAppli) { var item = new Claim("Application", appli.LibelleApplication); result.Add(item); } } return result; } }
问题排查与解决步骤
1. 修正ProfileService的Claims返回逻辑
IdentityServer4会根据客户端请求的Claim类型过滤返回结果,当前代码直接覆盖IssuedClaims未做过滤,可能导致部分Claims不被正确返回。修改GetProfileDataAsync方法:
public Task GetProfileDataAsync(ProfileDataRequestContext context) { var idUserCustom = context.Subject.FindFirst(x => x.Type == "IdUserCustom"); if (idUserCustom != null) { var utilisateur = _userRepository.GetUserCustomById(int.Parse(idUserCustom.Value)); if (utilisateur != null) { var listAppli = _userRepository.ListApplicationUser(utilisateur).OrderBy(x => x.RangAffichage).ToList(); var claims = GetClaims(utilisateur, listAppli); // 仅返回客户端请求的Claim类型 context.IssuedClaims = claims.Where(c => context.RequestedClaimTypes.Contains(c.Type)).ToList(); } } return Task.CompletedTask; }
2. 确认插件请求的Scopes
检查miniOrange插件配置界面的Scope字段,确保填写了openid profile email,只有请求了对应Scope,IdentityServer4才会返回相关Claims。
3. 配置插件的Claim映射
如果IdToken中已经包含邮箱、姓名等Claims,但插件测试界面未显示,需要在插件的Claim Mapping设置中,将name、family_name、email等字段映射到Wordpress对应的用户字段(如用户名、昵称、邮箱)。
4. 修复IsActiveAsync方法
当前方法未正确设置用户活跃状态,可能影响Claims返回,修改为:
public Task IsActiveAsync(IsActiveContext context) { var idUserCustom = context.Subject.FindFirst(x => x.Type == "IdUserCustom"); context.IsActive = idUserCustom != null; return Task.CompletedTask; }
5. 验证用户数据有效性
确认UserCustom实体中的PrenomUserCustom、NomUserCustom、EmailUserCustom字段是否有有效值,避免返回空字符串导致插件无法识别。
内容的提问来源于stack exchange,提问作者Space
相关产品推荐
相关产品推荐

