# فشل تسجيل الدخول عبر OIDC مع الاستجابات التي تحتوي على رمز الهوية فقط بعد ترقية OAuth2 2.x

**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:** [15 سبتمبر 2026، 7: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:** 1
**Showing post:** 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: [15 سبتمبر 2026، 7: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>

بعد تحديث بيئات Discourse المستضافة ذاتيًا في 11 سبتمبر 2026، بدأ تسجيل الدخول عبر OIDC في الفشل مع ظهور الخطأ التالي:

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

```

استمرت بيئة التطوير الأقدم في العمل بشكل سليم.

**البيئة**

- يعمل Discourse داخل Docker على آلة افتراضية Linux من Azure.
- 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، يكون الرمز الرئيسي فارغًا ويبقى معامل رمز المعرّف `"dummy-id"`.

مع OAuth2 2.0.25، يصبح الرمز الرئيسي `"dummy-id"` ويصبح معامل رمز المعرّف `nil`.

**الارتباط بفشل Discourse**

في الفرع الخاص بغياب userinfo، يبني إضافة OIDC الرمز باستخدام:

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

```

لاحقًا، تحاول:

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

```

عندما تستهلك المكتبة `id_token` كرمز رئيسي، فإن البحث عن المعامل اللاحق يعيد `nil`.

**حل مؤقت تم اختباره**

قمنا بتغيير بناء الرمز للحفاظ على رمز المعرّف من الاستجابة الأصلية:

```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 لتعافي النظام.

يحافظ هذا الحل على التحقق من المطالبات (claims) ورمز nonce الموجود مسبقًا، ويترك فرع userinfo دون تغيير.

لم يتم تضمين استجابة الرمز الحي الخام؛ تم إعادة إنتاج سلوك المكتبة باستخدام بيانات وهمية، وتم التحقق من الحل المؤقت من خلال تسجيل دخول حقيقي في بيئة UAT.

هل هذا مغطى بالفعل بإصلاح من المصدر (upstream fix)؟ وإلا، يمكنني تقديم طلب سحب (PR) مركّز مع تغطية للرجوع (regression coverage).

---

_[View the full topic](https://meta.discourse.org/t/oidc-login-fails-with-id-token-only-responses-after-oauth2-2-x-upgrade/412518)._
