📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

适用版本: 7.x-8.x 涉及组件: Elasticsearch Security / OIDC Realm

1. 错误说明 #

Userinfo Response did not contain a sub Claim 是 Elasticsearch 在使用 OpenID Connect (OIDC) 身份认证时抛出的安全异常。

当 Elasticsearch 配置了对接 OIDC IdP(身份提供商)的 Realm 后,完成初始 Token 交换流程、拿到 id_tokenaccess_token 之后,如果配置了 op.userinfo_endpoint,Elasticsearch 会主动向 IdP 的 UserInfo 端点发起请求,以获取更完整的用户声明(Claims)。

根据 OIDC 规范(OpenID Connect Standard 1.0)sub(Subject Identifier)是 UserInfo 响应中必须存在的核心字段,用于唯一标识用户身份。如果 Elasticsearch 在解析 UserInfo 响应时发现 sub 为空或不存在,出于安全考虑会直接拒绝该身份,并抛出此异常。

常见现象 #

  • 用户通过浏览器访问 Kibana 或 Elasticsearch 的 OIDC 登录入口后,重定向流程中断,页面报错或无限循环重定向。
  • Elasticsearch 日志中出现 ElasticsearchSecurityException: Userinfo Response did not contain a sub Claim
  • Kibana 显示 unable to authenticate userauthentication failed 类错误。
  • 该问题仅在使用 OIDC Realm 且启用 UserInfo 端点读取时出现,如果仅依赖 id_token 解析则不会触发。

典型报错日志 #

ElasticsearchSecurityException: Userinfo Response did not contain a sub Claim
	at org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectRealm.getUserInfo(OpenIdConnectRealm.java)
Caused by: IllegalStateException: subject claim is missing in userinfo response

2. 原因分析 #

从 Elasticsearch 源码逻辑来看,在校验 UserInfo 响应时,会首先调用 userInfoClaims.getSubject() 获取 sub 字段。如果返回结果为空,就立即抛出 ElasticsearchSecurityException

以下场景均可能导致该问题:

2.1 IdP 的 UserInfo 端点实现不规范 #

部分身份提供商(如内部自建 SSO、老旧 OAuth2 系统、或配置错误的 Keycloak / Dex / Auth0 实例)的 UserInfo 端点并未严格遵循 OIDC 规范,返回的 JSON 中可能包含 usernameemailuser_id 等字段,但唯独缺少 sub

2.2 网关或中间件裁剪了响应字段 #

在企业网络架构中,IdP 前面可能存在 API 网关、WAF、反向代理或自定义中间件。这些组件如果配置了响应体改写规则(例如只透传白名单字段),可能无意中将 sub 字段过滤掉。

2.3 Scope 配置不完整 #

OIDC 授权请求中的 scope 参数会影响 IdP 返回的用户信息范围。如果 scope 中没有包含 openid,或者 IdP 对 openid scope 的 UserInfo 返回内容做了特殊限制,也可能导致 sub 缺失。

2.4 用户属性映射配置错误 #

在 Elasticsearch 的 OIDC Realm 配置中,如果自定义了 claims.principalclaims.nameclaims.mail 等映射规则,但 IdP 实际返回的字段名与配置不匹配,也可能间接导致身份解析失败。

3. 解决方案 #

3.1 确认问题来源 #

第一步:直接调用 IdP 的 UserInfo 端点,检查原始响应

curl -s -H "Authorization: Bearer <your_access_token>" \
  https://your-idp.com/oauth2/userinfo \
  | jq .

检查返回的 JSON 中是否包含 sub 字段。例如,一个符合规范的响应应类似:

{
  "sub": "248289761001",
  "name": "Jane Doe",
  "email": "janedoe@example.com"
}

如果返回结果中没有 sub,说明问题在 IdP 侧。

第二步:对比 id_token 中的 sub

解码 id_token(可以使用 jwt.io 或命令行工具):

echo "<id_token>" | cut -d. -f2 | base64 -d 2>/dev/null | jq .

确认 id_token 中是否包含 sub。如果 id_tokensub 但 UserInfo 没有,则问题明确出在 UserInfo 端点实现上。

3.2 修复 IdP 配置 #

  • Keycloak:检查 Client 的 Mapper 配置,确保 sub 声明已映射到正确的用户属性(通常是 Subjectusername)。
  • Auth0:在 Auth0 Dashboard 的 User Management 中,确认 subuser_id 的映射已启用。
  • Dex / 内部 IdP:检查 Dex 的 connectors 配置,确保 id 字段在 UserInfo 响应中被正确填充。

3.3 绕过 UserInfo 端点(仅在必要时) #

如果 IdP 无法修复,可以修改 Elasticsearch 的 OIDC Realm 配置,不依赖 UserInfo 端点,仅从 id_token 中提取用户信息:

xpack.security.authc.realms.oidc.oidc1:
  order: 2
  ...
  claims:
    principal: sub
    name: name
    mail: email

同时移除或注释掉 op.userinfo_endpoint 相关配置(或确保 Elasticsearch 不会调用该端点)。

3.4 检查代理与中间件 #

如果 IdP 前面有 Nginx、Envoy、APISIX 等网关,检查响应改写规则:

# 示例:Nginx 中可能因 sub_filter 或 js_body_filter 导致字段丢失
# 检查是否有类似配置并临时移除后重试

4. 预防措施 #

  • IdP 对接前先做规范校验:在将任何 OIDC IdP 接入 Elasticsearch 之前,先用 curlOIDC Debug Tool 验证 UserInfo 端点返回内容是否符合规范。
  • 监控 OIDC 认证失败日志:在 Elasticsearch 日志中设置告警规则,当出现 Userinfo Response did not contain a sub Claim 时及时通知运维人员。
  • 优先使用 id_token 中的声明:如果 IdP 的 UserInfo 端点稳定性或规范性无法保证,建议直接在 OIDC Realm 中配置从 id_token 获取用户声明,减少对 UserInfo 端点的依赖。
  • 在测试环境充分验证:每次升级 IdP、修改网关配置或调整 OIDC Client 设置后,重新走一遍完整的登录流程并抓取 UserInfo 响应进行验证。

5. 小结 #

Userinfo Response did not contain a sub Claim 的本质不是 Elasticsearch 的 Bug,而是 IdP 的 UserInfo 响应不符合 OIDC 规范导致的安全拒绝。排查重点应放在 IdP 的 UserInfo 端点实现本身,而非 Elasticsearch 侧。

通过直接调用 UserInfo 端点确认返回内容、对比 id_token 中的 sub、检查网关中间件配置,通常可以快速定位根因。在无法修改 IdP 行为的情况下,调整 Elasticsearch OIDC Realm 配置、仅依赖 id_token 解析用户身份,是一个可行的替代方案。

相关错误 #

附:日志上下文 #

// Elasticsearch 源码中校验 UserInfo 响应的核心逻辑
if (userInfoClaims.getSubject().isEmpty()) {
    claimsListener.onFailure(
        new ElasticsearchSecurityException("Userinfo Response did not contain a sub Claim")
    );
    return;
}