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

**URL:** https://meta.discourse.org/t/oidc-login-fails-with-id-token-only-responses-after-oauth2-2-x-upgrade/412518
**Category:** Bug
**Tags:** openid-connect
**Created:** [2026年九月15日 19:59 UTC](https://meta.discourse.org/t/oidc-login-fails-with-id-token-only-responses-after-oauth2-2-x-upgrade/412518 "2026-09-15T19:59:13Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![levarm](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/levarm/32/577865_2.png) [@levarm](https://meta.discourse.org/u/levarm)
#### Post date: [2026年九月15日 19:59 UTC](https://meta.discourse.org/t/oidc-login-fails-with-id-token-only-responses-after-oauth2-2-x-upgrade/412518/1 "2026-09-15T19:59:14Z")

</div>

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

```plaintext
(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 基础设施或真实凭证：

```plaintext
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 插件使用以下方式构建令牌：

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

```

随后，它尝试执行：

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

```

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

**已测试的临时解决方案**

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

```plaintext
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。

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [2026年九月15日 21:21 UTC](https://meta.discourse.org/t/oidc-login-fails-with-id-token-only-responses-after-oauth2-2-x-upgrade/412518/2 "2026-09-15T21:21:15Z")

</div>

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

> <https://github.com/discourse/discourse/commit/a3e135f77e95ee2d289d05bce17d179d74143cfd>
>
> Some providers return a token response with no access token at all, or
> with an e…mpty \`access\_token\` alongside the \`id\_token\`. \`oauth2\` 2.x
> treats \`id\_token\` as one of the keys that can hold the token value, so
> \`AccessToken.from\_hash\` either takes the \`id\_token\` and removes it from
> the params, leaving \`access\_token\["id\_token"\]\` nil and the JWT decode
> failing, or it takes the empty \`access\_token\` and raises
> "OAuth2::AccessToken has no token".
> 
> When userinfo is disabled the strategy reads everything it needs from
> the \`id\_token\`, so build the access token directly and keep the whole
> parsed response in the params.
> 
> \- The spec for the userinfo-disabled path goes back to an
> \`id\_token\`-only token response, which is what a provider with no
> userinfo endpoint sends.
> \- A new case covers a response with a blank \`access\_token\`.

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

---

<div class="post-metadata">

### Author: ![levarm](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/levarm/32/577865_2.png) [@levarm](https://meta.discourse.org/u/levarm)
#### Post date: [2026年九月16日 11:08 UTC](https://meta.discourse.org/t/oidc-login-fails-with-id-token-only-responses-after-oauth2-2-x-upgrade/412518/3 "2026-09-16T11:08:30Z")

</div>

谢谢。问题已解决。

---

<div class="post-metadata">

### Author: ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)
#### Post date: [2026年九月17日 01:39 UTC](https://meta.discourse.org/t/oidc-login-fails-with-id-token-only-responses-after-oauth2-2-x-upgrade/412518/4 "2026-09-17T01:39:38Z")

</div>


