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

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客户端配置截图

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 20:22:02