在 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。