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

适用版本: 7.x-8.9

1. 错误异常的基本描述 #

Failed to get user information from the UserInfo endpoint. Code=[...]; Description=[...] 表示 Elasticsearch 确实收到了 UserInfo Endpoint 的 HTTP 响应,但响应不是成功状态(非 2xx),并且还能提取出明确的错误码和描述。这类报错比通用的 UserInfo 失败更具体,因为它通常意味着身份提供方已经返回了可解释的认证或授权错误。

源码表明 Elasticsearch 会优先从 WWW-Authenticate 响应头解析 Bearer Token 错误;如果没有这个头,就回退到 HTTP 状态码和响应描述。

常见现象 #

  • OIDC 登录失败,而且日志中会出现 invalid_tokeninsufficient_scopeinvalid_request 等错误码。
  • 身份提供方访问正常(网络连通性没问题),但 Elasticsearch 仍然拒绝完成登录。
  • 相同 Realm 下,不同用户或不同 token 的行为可能不一致:有些用户能登录,有些不能。
  • 在 Kibana 或其他依赖 OIDC 的应用中,用户看到登录失败页面,但没有明确的错误提示。
  • 从 Elasticsearch 日志可以看到完整的错误码和描述,例如:Code=[401]; Description=[invalid_token]

典型报错与异常栈 #

常见日志形态通常类似下面这样:

ElasticsearchSecurityException: Failed to get user information from the UserInfo endpoint. Code=[401]; Description=[invalid_token]
	at org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectRealm...

或者 scope 不足:

ElasticsearchSecurityException: Failed to get user information from the UserInfo endpoint. Code=[403]; Description=[insufficient_scope]
	at org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectRealm...

或者从 HTTP 状态码提取:

ElasticsearchSecurityException: Failed to get user information from the UserInfo endpoint. Code=[400]; Description=[Bad Request]
	at org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectRealm...

2. 为什么会发生这个错误 #

Failed to get user information from the UserInfo endpoint. Code=[...] 的根因是"OIDC UserInfo Endpoint 返回了错误响应"。Elasticsearch 向 IdP 的 UserInfo Endpoint 发起请求后,IdP 返回了非 2xx 的 HTTP 状态码,并在 WWW-Authenticate 头或响应体中提供了错误详情。

常见原因通常包括:

  • access token 已过期access_token 的有效期已过,IdP 返回 invalid_token 错误。
  • access token 无效access_token 格式错误、被篡改或不是由当前 IdP 签发的。
  • token 受众(audience)不匹配:token 的 aud 声明与 IdP 预期的受众不一致。
  • scope 不足:当前 token 没有访问 UserInfo Endpoint 所需的 openidprofile scope。
  • 身份提供方要求额外认证条件:如需要 MFA(多因素认证)、step-up authentication 等,但当前请求不满足。
  • 反向代理或网关问题:反向代理、SSO 网关或 WAF 重写了认证头,导致 Elasticsearch 拿到的是不完整或错误的认证信息。
  • 时钟偏移(clock skew):Elasticsearch 节点与 IdP 之间的系统时钟不一致,导致 token 的 iat(签发时间)或 exp(过期时间)验证失败。
  • token 缓存问题:客户端或中间件缓存了旧的、已失效的 token,并继续使用它访问 UserInfo Endpoint。

3. 如何排查和解决这个异常和解决这个异常 #

建议按"先解读错误码、再检查 token、后排查网络"的顺序处理:

  1. 解读错误码和描述:直接查看日志里的 CodeDescription,先按身份提供方错误语义定位。

    # 查看 Elasticsearch 安全日志中的错误详情
    grep -r "Failed to get user information from the UserInfo endpoint" /var/log/elasticsearch/
    grep -r "Code=" /var/log/elasticsearch/ | tail -20
    
  2. 检查 token 有效性:确认 token 的 scope、audience、过期时间和签发流程是否符合 UserInfo 要求。

    # 解码 JWT token(如果使用的是 JWT 格式的 access_token)
    echo "your_access_token" | cut -d'.' -f2 | base64 -d | jq .
       
    # 检查 token 的 exp(过期时间)、aud(受众)、scope 等声明
    
  3. 对照身份提供方日志:确认该请求为何返回了当前 HTTP 状态和认证头。

    # 如果是 Keycloak,查看 Keycloak 日志
    docker logs keycloak_container | grep -i "userinfo"
       
    # 如果是 Okta/Auth0,查看其管理控制台的事件日志
    
  4. 检查中间代理:检查反向代理、SSO 网关或 WAF 是否透传了 AuthorizationWWW-Authenticate 相关头部。

    # 检查代理配置(以 Nginx 为例)
    grep -r "proxy_set_header" /etc/nginx/
       
    # 确认 Authorization 头没有被过滤
    
  5. 检查时钟同步:如果错误只在某些客户端或节点上出现,检查是否存在 clock skew。

    # 检查节点时间
    date
       
    # 与 NTP 服务器同步时间
    ntpdate -q pool.ntp.org
    
  6. 检查 token 缓存:如果错误只在某些客户端上出现,检查是否存在缓存旧 token 或错误回调地址。

排查时需要注意的问题 #

  • 这个异常已经把线索收敛到了"身份提供方明确告诉你为什么失败"。排查时最有价值的信息就是 CodeDescription,它们通常比通用堆栈更接近根因。
  • 不同的错误码指向不同的问题:invalid_token 通常是 token 过期或无效,insufficient_scope 是 scope 配置问题,invalid_request 可能是请求格式问题。
  • 需要同时检查 Elasticsearch 配置和 IdP 配置,问题可能出在任何一端。

4. 如何解决这个错误 #

常用修复思路 #

  • 修复 OIDC 客户端配置:确保 scope、audience 和回调流程正确。

    # elasticsearch.yml 中的 OIDC Realm 配置
    xpack.security.authc.realms.oidc.oidc1:
      order: 2
      rp.client_id: "your-client-id"
      rp.client_secret: "your-client-secret"
      rp.response_type: "code"
      rp.redirect_uri: "https://your-elasticsearch-domain:9243/api/security/v1/oidc"
      op.issuer: "https://your-idp-domain.com"
      op.authorization_endpoint: "https://your-idp-domain.com/oauth2/authorize"
      op.token_endpoint: "https://your-idp-domain.com/oauth2/token"
      op.userinfo_endpoint: "https://your-idp-domain.com/oauth2/userInfo"
      op.jwkset_path: "https://your-idp-domain.com/oauth2/jwks"
      # 确保请求的 scope 包含 openid 和 profile
      rp.requested_scopes: ["openid", "profile", "email"]
    
  • 缩短 token 缓存时间:避免使用已失效凭证访问 UserInfo Endpoint。

    # 在 OIDC Realm 配置中调整缓存时间(如果支持)
    # 或者在客户端(如 Kibana)配置更短的 token 刷新间隔
    
  • 修复反向代理配置:确保代理、SSO 网关或 WAF 不会篡改或裁剪认证头。

    # Nginx 代理配置示例(确保透传 Authorization 头)
    location / {
        proxy_pass http://elasticsearch:9200;
        proxy_set_header Authorization $http_authorization;
        proxy_pass_header Authorization;
    }
    
  • 解决时钟偏移问题:同步 Elasticsearch 节点和 IdP 的系统时钟。

    # 安装并配置 NTP
    yum install ntp
    systemctl enable ntpd
    systemctl start ntpd
      
    # 手动同步
    ntpdate pool.ntp.org
    
  • 联系 IdP 管理员:如果错误码指向 IdP 侧的问题(如用户状态异常、IdP 配置错误),需要联系 IdP 管理员解决。

后续注意事项与推荐建议 #

  • 为 OIDC 认证配置合理的 token 刷新策略,避免 access_token 过期导致的认证失败。
  • 建立对 IdP 服务可用性和错误响应的监控,在 IdP 返回异常错误时及时预警。
  • 定期检查 OIDC 客户端配置,确保 scope、audience 等参数与 IdP 的要求保持一致。
  • 在应用层(如 Kibana)实现适当的错误处理和用户提示,帮助用户理解认证失败的原因。

借助 INFINI 产品提升排障效率 #

  • INFINI Console 适合查看集群的安全配置、认证日志和用户访问趋势,帮助快速判断 Code=[...] 错误的分布和模式,并提供可视化的配置管理和审计功能,追踪 OIDC 配置变更历史。
  • INFINI Gateway 适合部署在 Elasticsearch 前面做请求观测和流量治理,可以记录所有认证相关请求的详细日志,包括 OIDC 流程中每个步骤的请求和响应,帮助定位是 Elasticsearch 配置问题还是 IdP 返回问题,同时提供请求重试和熔断机制来提升认证稳定性。
  • 建议将 OIDC 认证相关指标(成功率、错误码分布、token 过期率)统一接入监控面板,结合 INFINI Console 的告警功能,在认证质量下降时及时通知。

5. 小结 #

Failed to get user information from the UserInfo endpoint. Code=[...]; Description=[...] 已经把线索收敛到了"身份提供方明确告诉你为什么失败"。排查时最有价值的信息就是 CodeDescription,它们通常比通用堆栈更接近根因。大多数情况下,这个问题可以通过解读错误码、检查 token 有效性和修复 OIDC 配置来解决。

只要把 OIDC 配置校验、token 管理和 IdP 监控固定下来,大多数认证错误码类异常都可以被快速定位和恢复,也更容易通过 INFINI Console 和 INFINI Gateway 实现持续防护。

相关错误 #

参考文档 #

附:日志上下文 #

下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:

} else {
        final Header wwwAuthenticateHeader = httpResponse.getFirstHeader("WWW-Authenticate");
        if (Strings.hasText(wwwAuthenticateHeader.getValue())) {
            BearerTokenError error = BearerTokenError.parse(wwwAuthenticateHeader.getValue());
            claimsListener.onFailure(
                new ElasticsearchSecurityException("Failed to get user information from the UserInfo endpoint. Code=[{}]; " +
                    "Description=[{}]"; error.getCode(); error.getDescription()));
        } else {
            claimsListener.onFailure(
                new ElasticsearchSecurityException("Failed to get user information from the UserInfo endpoint. Code=[{}]; " +
                    "Description=[{}]"; httpResponse.getStatusLine().getStatusCode();