بعد تحديث بيئات Discourse المستضافة ذاتيًا في 11 سبتمبر 2026، بدأ تسجيل الدخول عبر OIDC في الفشل مع ظهور الخطأ التالي:
(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 التحتية أو بيانات اعتماد حقيقية:
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 الرمز باستخدام:
::OAuth2::AccessToken.from_hash(client, response.parsed)
لاحقًا، تحاول:
::JWT.decode(access_token["id_token"], nil, false).first
عندما تستهلك المكتبة id_token كرمز رئيسي، فإن البحث عن المعامل اللاحق يعيد nil.
حل مؤقت تم اختباره
قمنا بتغيير بناء الرمز للحفاظ على رمز المعرّف من الاستجابة الأصلية:
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).