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

适用版本: 7.x-8.x

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

Failed to verify access token. ID Token doesn't contain at_hash claim 表示 Elasticsearch 在 OIDC(OpenID Connect)认证流程中,尝试对 Access Token 进行完整性校验时失败。根据 OIDC 规范,at_hash(Access Token Hash)是 ID Token 中的一个可选声明,用于让依赖方(Relying Party)校验 Access Token 的完整性。当 Elasticsearch 的 OIDC Realm 配置要求验证 Access Token,但 IdP(身份提供商)返回的 ID Token 中缺少 at_hash 声明时,就会触发此异常。

常见现象 #

  • 用户通过 OIDC 登录 Elasticsearch 或 Kibana 时,在认证回调阶段失败,页面报错或无限重定向。
  • Elasticsearch 日志中出现 Failed to verify access token. ID Token doesn't contain at_hash claim 异常信息。
  • 多见于 implicit flowhybrid flow,且配置中同时返回了 id_tokenaccess_token 的场景。
  • 部分用户正常登录,部分用户失败,取决于 IdP 针对不同的授权请求是否签发了 at_hash

典型报错与异常栈 #

if (Strings.hasText(idTokenClaimsSet.getStringClaim("at_hash")) == false) {
    listener.onFailure(new ElasticsearchSecurityException(
        "Failed to verify access token. ID Token doesn't contain at_hash claim"
    ));
}

实际日志中可能出现如下堆栈信息:

[2026-04-04T10:00:00,000][WARN ][o.e.x.s.a.o.OIDCIdpAuthenticator] [node-1] Failed to verify access token
ElasticsearchSecurityException: Failed to verify access token. ID Token doesn't contain at_hash claim
    at org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectAuthenticator.validateAccessToken(OpenIdConnectAuthenticator.java:XXX)
    at org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectAuthenticator.authenticate(OpenIdConnectAuthenticator.java:XXX)

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

at_hash 是 OIDC 规范( OpenID Connect Core 1.0 - Section 3.3.2.11)中定义的一个声明。其作用是:当 ID Token 与 Access Token 同时返回时,客户端可以通过 at_hash 中的哈希值校验 Access Token 是否被篡改。

Elasticsearch 的 OIDC Realm 在以下情况下会触发 at_hash 校验:

  • Realm 配置中启用了 opaque_id 或 Access Token 验证相关的选项。
  • 授权响应类型为 id_token token(implicit flow)或 code token / code id_token token(hybrid flow)。

常见原因包括:

  • IdP 未签发 at_hash:部分 IdP(如某些旧版本 Keycloak、Auth0 特定配置、或自定义 OIDC 实现)在返回 Access Token 时未按要求计算并写入 at_hash 声明。
  • 授权流程配置不匹配:Elasticsearch OIDC Realm 的 response_type 配置为 id_token,但 IdP 实际返回了包含 Access Token 的响应,或反之。
  • Token 被中间件修改:如果在 Elasticsearch 之前部署了网关、反向代理或自定义鉴权层(如 Nginx Lua、Envoy ext_authz),这些中间件可能对 ID Token 做了重签、解码再编码或裁剪,导致 at_hash 声明丢失。
  • ID Token 来源不正确:使用了非 IdP 直接签发的 ID Token(例如从缓存、Session 或第三方服务中获取的旧 Token)。

3. 如何排查此异常 #

建议按以下步骤逐一排查:

  1. 解码 ID Token:将登录回调中收到的 ID Token(JWT 格式)进行 Base64 解码,检查其 payload 中是否包含 at_hash 字段。

    # 解码 ID Token(JWT 第二段)
    echo "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYXRfaGFzaCI6IndpVFRqMTVXSVFpWE1mZlB1YkRNdyIsImlzcyI6Imh0dHBzOi8vaWRwLmV4YW1wbGUuY29tIiwiYXVkIjoiY2xpZW50LWlkIiwiZXhwIjoxNzA4MDAwMDAwfQ.signature" \
      | cut -d'.' -f2 | base64 -d | jq .
    
  2. 核对 OIDC Realm 配置:检查 elasticsearch.yml 中 OIDC Realm 的 response_type 设置,确认其与 IdP 侧配置的授权流程一致。

  3. 检查 IdP 的 OIDC 配置:查阅 IdP 文档,确认在当前授权流程下是否应该返回 at_hash。例如,Keycloak 在 implicithybrid flow 下默认会签发 at_hash,但某些配置可能禁用此行为。

  4. 确认无中间件篡改:如果架构中存在网关或自定义鉴权层,检查是否对 Token 做了处理。可以对比客户端直接收到的原始 ID Token 与 Elasticsearch 侧收到的 Token 是否一致。

  5. 查看完整日志:在 elasticsearch.yml 中临时开启 DEBUG 日志,获取更详细的 OIDC 认证上下文:

    logger.org.elasticsearch.xpack.security.authc.oidc: DEBUG
    

4. 如何解决这个错误 #

方案一:调整 IdP 配置,使其签发 at_hash #

这是最推荐的解决方案。以 Keycloak 为例,确保:

  • 客户端(Client)的 Standard Flow EnabledImplicit Flow Enabled 配置与 Elasticsearch OIDC Realm 的 response_type 匹配。
  • 在客户端设置中,确认 ID Token Signature AlgorithmAccess Token Signature Algorithm 配置正确,且 IdP 在同时返回 ID Token 和 Access Token 时自动计算 at_hash

Keycloak 相关配置示例(通过 Admin Console):

Clients -> your-client -> Settings:
  - Standard Flow Enabled: ON
  - Implicit Flow Enabled: OFF(如使用 authorization_code flow)
  - ID Token Signature Algorithm: RS256

方案二:调整 Elasticsearch OIDC Realm 配置 #

如果 IdP 确实无法签发 at_hash,可考虑调整 Elasticsearch 侧配置,避免触发 Access Token 校验。在 elasticsearch.yml 中检查 OIDC Realm 配置:

xpack.security.authc.realms.oidc.oidc1:
  type: oidc
  order: 2
  idp.metadata.path: "https://idp.example.com/.well-known/openid-configuration"
  idp.client_id: "elasticsearch-client"
  idp.client_secret: "your-client-secret"
  rp.response_type: "code"          # 使用 authorization_code,避免 implicit/hybrid flow
  rp.redirect_uri: "https://es.example.com/api/security/v1/oidc"
  claims.principal: "sub"

rp.response_type 设置为 code(默认值),可以规避 at_hash 校验问题,因为 authorization_code flow 中 Access Token 通过后端 token endpoint 换取,不在 ID Token 中同时返回。

方案三:检查并修正中间件行为 #

如果使用 INFINI Gateway 或其他网关在 Elasticsearch 前端做认证预处理,确保:

  • 不对 ID Token 进行重签或 payload 修改。
  • 如需要注入自定义声明,使用 x- 前缀的私有声明,不删除标准声明如 at_hashisssubaudexpiat

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

  • 统一 OIDC 授权流程:避免在同一个 IdP 客户端上同时启用多种不兼容的授权流程,减少配置歧义。
  • 定期校验 Token 内容:在升级 IdP、网关或 Elasticsearch 版本后,重新解码 ID Token 确认关键声明(isssubaudexpiatat_hash)完整无误。
  • 监控认证失败率:通过 INFINI Console 监控 OIDC 认证失败趋势,及时发现因 at_hash 缺失或其他 Token 问题导致的登录异常。

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

  • INFINI Console 适合查看 Elasticsearch 集群的认证日志、安全事件趋势和请求画像,帮助快速判断 OIDC 登录失败是配置问题还是 IdP 侧问题。
  • INFINI Gateway 适合部署在 Elasticsearch 前面做请求观测、认证透传和流量治理。在 OIDC 场景下,Gateway 可以记录完整的 Token 交换过程,便于对比客户端与服务器端收到的 ID Token 是否一致。

5. 小结 #

Failed to verify access token. ID Token doesn't contain at_hash claim 是一个与 OIDC 协议实现细节高度相关的异常。核心问题始终是 at_hash 声明缺失,排查时应围绕 ID Token 内容、IdP 签发策略和 Elasticsearch OIDC Realm 配置三条线索展开。首选方案是调整 IdP 使其按规范签发 at_hash;其次是通过将 response_type 切换为 code 来规避该问题。

相关错误 #

参考链接 #

附:日志上下文 #

if (Strings.hasText(idTokenClaimsSet.getStringClaim("at_hash")) == false) {
    listener.onFailure(new ElasticsearchSecurityException(
        "Failed to verify access token. ID Token doesn't contain at_hash claim"
    ));
}