升级到 OAuth2 2.x 后,OIDC 登录因仅返回 ID Token 而失败

在 2026 年 9 月 11 日更新我们的自托管 Discourse 环境后,OIDC 登录开始失败,报错如下:

(oidc) Authentication failure! jwt_decode_failed:
JWT::DecodeError, Nil JSON web token

我们的旧开发环境仍然可以正常工作。

环境信息

  • Discourse 运行在 Azure Linux 虚拟机上的 Docker 中。
  • 流量路径:Azure Front Door → 内部负载均衡器 → 虚拟机。
  • Discourse 托管在 /forum 路径下。
  • 使用 Azure AD B2C 自定义策略进行 OIDC 认证。
  • 虚拟机出站流量通过 Azure Firewall 路由。
  • 出错的 Discourse 版本:74f839fb7
  • 正常工作的开发版本:502aa3687

相关的依赖版本如下:

依赖项 正常工作的开发环境 出错的生产环境
oauth2 1.4.11 2.0.25
omniauth-oauth2 1.7.3 1.9.0
jwt 2.10.1 3.2.0

更新后的仓库包含提交 d881bf2d4aabcf2430863a96beafaeabaf915702,“DEPS: Upgrade oauth2 to 2.x”(#43523)。

最小复现步骤

此复现无需网络连接、Azure 基础设施或真实凭证:

require "oauth2"

puts Gem.loaded_specs.fetch("oauth2").version

client = OAuth2::Client.new(
  "dummy",
  "dummy",
  site: "https://example.invalid"
)

token = OAuth2::AccessToken.from_hash(
  client,
  {"id_token" => "dummy-id"}
)

puts "Main token: #{token.token.inspect}"
puts "ID token parameter: #{token["id_token"].inspect}"

在使用 OAuth2 1.4.11 时,主令牌为空,ID 令牌参数保持为 "dummy-id"

在使用 OAuth2 2.0.25 时,主令牌变为 "dummy-id",而 ID 令牌参数变为 nil

与 Discourse 故障的关联

在 no-userinfo 分支中,OIDC 插件使用以下方式构建令牌:

::OAuth2::AccessToken.from_hash(client, response.parsed)

随后,它尝试执行:

::JWT.decode(access_token["id_token"], nil, false).first

当库将 id_token 用作主令牌时,后续的参数查找返回 nil

已测试的临时解决方案

我们修改了令牌构建逻辑,以保留原始响应中的 ID 令牌:

payload = response.parsed
token = ::OAuth2::AccessToken.from_hash(client, payload)
token.params["id_token"] = payload["id_token"] if payload["id_token"]
token

应用此补丁并重启 UAT 容器后,登录成功。此次恢复无需对 Front Door、防火墙或 B2C 进行任何更改。

此方案保留了现有的声明(claim)和 nonce 验证,并且未更改 userinfo 分支。

未包含原始实时令牌响应;库的行为是使用假数据复现的,而临时解决方案已通过真实的 UAT 登录进行了验证。

这是否已被上游修复覆盖?如果没有,我可以提交一个包含回归测试覆盖的针对性 PR。

1 个赞

此问题应已通过以下提交修复:

部署该提交后,如果您仍然遇到问题,请告知我们。

7 个赞

谢谢。问题已解决。

1 个赞