الخلاصة

  • إذا تغير المفتاح بين قراءة Session وإنشاء اشتراك الدفع، فقد ترفض خدمة الدفع رسالة التحقق. لا يجوز للخادم إعادة محاولة هذا التحقق المرفوض، وعلى العميل فحص sessionState الوارد في الاستجابة.
  • يقترح بلاغ التصحيح التقني 9055 حذف مسار التنبيه الأخير الاختياري في RFC 9749. ظل البلاغ في حالة Reported عند مراجعته، ولذلك لا يمثل تعديلاً معيارياً مؤكداً ولا يجوز تجاهل الإشكال الذي يطرحه.

ليس كل انتظار دليلاً على أن العمل ما زال جارياً. قد يكون الطرف الذي ننتظر منه الخطوة التالية قد انتهى بالفعل، أو قد تكون القواعد تمنعه من تكرار تلك الخطوة. عندئذ لا تكفي زيادة مهلة الانتظار، لأن المشكلة ليست بطئاً في التنفيذ بل غياب من يتولى الاستعادة.

تصف RFC 9749، المنشورة في مارس 2025، حالة محددة من هذا النوع في JMAP Web Push. تضيف الوثيقة مصادقة VAPID، وتعالج احتمال تبديل مفتاح خادم التطبيق أثناء إنشاء اشتراك. قد يصل طلب الإنشاء إلى الخادم وتعود استجابته، لكن رسالة التحقق ترفض لأن نقطة استقبال الدفع مرتبطة بالمفتاح السابق. في هذه الحالة لا يجوز للخادم إعادة محاولة PushVerification المرفوضة.

هذه ليست رواية عن عطل مثبت لدى شركة معينة. المصادر التي راجعناها لا تحدد مزوداً متضرراً أو نسبة إخفاق أو عدداً من رسائل البريد المفقودة. لكنها تسمح باختبار تعريف الإدارة لكلمة «اكتمل»: هل يعني أن الخادم بدأ استخدام المفتاح الجديد، أم أن العميل يستطيع الخروج من الحالة التي خلفها التغيير؟

معلومة صحيحة فقدت صلاحيتها أثناء الإجراء

يعرف العميل المفتاح العام لخادم التطبيق من كائن Session، ثم يستخدم نقطة دفع مرتبطة بذلك المفتاح عند تسجيل الاشتراك في JMAP. لا تقع قراءة الجلسة وطلب PushSubscription/set ضمن عملية واحدة لا يمكن أن يتخللها تغيير.

إذا بدل الخادم المفتاح بين الخطوتين، فقد توقع رسالة التحقق بالمفتاح الجديد، بينما تظل نقطة الدفع مقيدة بالمفتاح العام القديم. الرفض برمز 403 في السيناريو الذي تعرضه RFC 9749 يعكس اختلافاً بين لحظتين، لا مجرد خطأ حسابي في التوقيع يمكن إصلاحه بتكرار الحساب نفسه.

هذا الارتباط مقصود. تشترط RFC 8292 أن تثبت المصادقة على الاشتراك المقيد امتلاك المفتاح الخاص الموافق للمفتاح العام المستخدم عند إنشائه. لو أمكن للمرسل استبدال ذلك الالتزام بأي مفتاح يختاره لاحقاً، لفقد القيد وظيفته. لذلك يتطلب تبديل المفتاح العام اشتراكاً جديداً، وليس مجرد توقيع جديد على وجهة قديمة.

لكن 403 لا يشخص هذه الحالة بمفرده. فقد يؤدي توقيع غير صالح أو جمهور مستهدف غير صحيح في الرمز أو مشكلة في صلاحيته الزمنية إلى رفض المصادقة أيضاً. لا بد من وصل توقيت الإنشاء بجيل المفتاح وحالة الجلسة قبل نسب الإخفاق إلى سباق التبديل. إعادة بناء كل الاشتراكات عند كل رفض قد تضاعف الطلبات من دون إزالة السبب الحقيقي.

ويجب أيضاً التمييز بين وظائف المفاتيح. توقيع VAPID يثبت هوية خادم التطبيق، أما تشفير محتوى الإشعار فيعالج السرية. ينبغي أن يختلف المفتاح الخاص للتوقيع عن المفتاح الخاص المستخدم في تبادل مفاتيح التشفير. نجاح المصادقة لا يثبت وصول الإشعار أو قراءته أو اتخاذ المستخدم إجراءً بسببه.

الاستجابة المقبولة لا تعني قناة جاهزة

تفرض RFC 8620 تحققاً أولياً من قدرة العميل على الوصول إلى البيانات المرسلة إلى العنوان الذي قدمه. يجب أن يعيد رمز التحقق الصحيح قبل أن يرسل الخادم الطلبات اللاحقة. ولهذا توجد مسافة حقيقية بين قبول إنشاء الاشتراك واكتمال التحقق منه.

موضع الفحص هنا مهم. لا تتضمن PushSubscription/set شرط ifInState، ولا تحتوي نتيجتها على oldState أو newState. لا يصح استعارة آلية التحكم في تعارض تحديث مجموعة بيانات عادية وافتراض وجودها في هذه الواجهة. المعلومة ذات الصلة هي sessionState في الاستجابة الخارجية لواجهة API، وتشير إلى حالة كائن Session.

تلزم RFC 9749 العميل بفحص هذه الحالة في ضوء applicationServerKey التي كان يتوقعها. وعند اكتشاف الاختلاف تسمح له بمحاولة إنشاء اشتراك جديد وبإزالة المحاولة السابقة غير المكتملة. المقصود هو البدء من معلومة محدثة، لا تكرار الطلب ذاته إلى ما لا نهاية مع الاحتفاظ بالافتراض الذي فشل.

ومن المهم تضييق معنى منع إعادة المحاولة. يتعلق المنع برسالة التحقق المرفوضة في هذه الحالة، ولا يقرر حظراً عاماً على إعادة إرسال كل إشعارات الدفع. إعادة إنشاء علاقة صحيحة من جانب العميل ليست هي إصرار الخادم على اختبار العلاقة القديمة بالشروط نفسها.

يمكن أن تخفي لوحة تشغيل هذه المسافة إذا جمعت كل شيء تحت عنوان «مشترك». هل قبل الطلب؟ هل عاد رمز التحقق؟ هل ثبتت العلاقة مع جيل المفتاح الحالي؟ ليس ضرورياً عرض هذه التفاصيل كلها للمستخدم، لكن ينبغي أن يستطيع المشغل التمييز بينها عند غياب التنبيهات.

فالصمت على الشاشة قد يعني عدم وجود تغييرات جديدة، وقد يعني أن قناة الإشعار لم تصبح صالحة أصلاً. عد الطلبات المقبولة لا يكفي للفصل بين الحالتين. كما أن مطالبة المستخدم بإعادة تثبيت التطبيق، إن استعملت كحل عام، لا تثبت وحدها سلامة مسار الاستعادة؛ ربما غيرت الحالة المحلية من دون تفسير المرحلة التي توقفت. هذا احتمال تشغيلي يستحق الفحص، وليس ممارسة ننسبها هنا إلى مزود بعينه.

التنبيه الأخير ليس ضمانة أخيرة

تتيح RFC 9749 فترة انتقالية اختيارية تستمر خلالها الاشتراكات القديمة باستخدام مفاتيحها المقابلة. عند انتهاء الفترة، أو فوراً إن لم توجد فترة انتقالية، يجب على الخادم تدمير الاشتراكات المرتبطة بالمفتاح القديم. لا تحدد الوثيقة مدة موحدة واجبة لكل البيئات.

ويعرض النص الأصلي أيضاً رسالة StateChange أخيرة اختيارية لبعض الاشتراكات، يفترض أن تدفع العميل إلى استدعاء PushSubscription/changes ثم اكتشاف حالة الجلسة الجديدة. هذه السلسلة هي موضع اعتراض تقني محدد، وليست تفصيلاً يمكن تحويله إلى ضمانة تشغيلية من دون مراجعة.

يسجل بلاغ التصحيح 9055 لدى RFC Editor، المقدم من Neil Jenkins في 31 يوليو 2026، اقتراح حذف الفقرة. يوضح أن PushSubscription غير مرتبط بحساب يسمح بإدراجه في بنية StateChange الموصوفة، وأن RFC 8620 لا تعرف أصلاً طريقة باسم PushSubscription/changes.

عند الاطلاع في 7 سبتمبر 2026، كانت الحالة Reported وليست Verified. لذلك لا يجوز القول إن التصحيح أصبح نصاً معيارياً نافذاً. ومع ذلك يمكن التحقق من ملاحظتي الواجهة في RFC 8620 نفسها. الاستنتاج العملي محدود: لا تعرض سلسلة الاستدعاءات محل الاعتراض باعتبارها مسار توافق مثبتاً، ولا تستنتج من البلاغ أن كل تنفيذ منشور معطل.

حتى لو وجد تنبيه صحيح تقنياً، فلن يضمن إيقاظ جهاز غير متاح. تحدد RFC 8030 عمر الاحتفاظ برسائل الدفع وتسمح للخدمة بتقصيره. الرسالة ذات العمر الصفري لا تنتظر عودة جهاز غائب. تسمية المرسل لها «الأخيرة» لا تضيف قدرة استقبال لدى الطرف الآخر.

لهذا ينبغي اختبار عودة العميل الذي لم ير أي تحذير: هل يكتشف التغيير حين ينشط ويستطيع إنشاء اشتراك بديل والتحقق منه؟ تسجيل قدرة JMAP لدى IANA يوثق المعرف المتفق عليه، لكنه لا يثبت انتشارها ولا يشهد على سلوك إصدارات العملاء الفعلية.

أدلة كافية من دون أرشيف ثان للأسرار

يمكن تتبع جيل المفتاح، ووقت قبول المحاولة، وإتمام التحقق، ثم سحب الاشتراك. لا يتطلب ذلك الاحتفاظ الدائم بعنوان الدفع الكامل أو أسرار التشفير.

تفرض RFC 8620 المحو الآمن لهذه القيم عند تدمير الاشتراك، ولا تسمح بإعادتها بواسطة PushSubscription/get حتى لو طلبها المستدعي صراحة. تستطيع معرفات الأجيال غير السرية والطوابع الزمنية ذات مدة الاحتفاظ المحدودة توثيق المسار. أما نسخ كل شيء إلى سجل تشخيصي دائم فتعطي مادة مسحوبة عمراً إضافياً خارج النظام الذي أزالها.

يرتبط عمر الاشتراك أيضاً ببيانات اعتماد API التي أنشأته. انتهاء تلك البيانات أو إلغاؤها ليس مجرد مشكلة نقل أخرى. ولا ينبغي للعميل تعديل اشتراكات لا يعرف deviceClientId الخاص بها بوصفه تابعاً له.

إصلاح جهاز واحد بمحو اشتراكات الأجهزة الأخرى يوسع نطاق التدخل ويصنع انقطاعاً جديداً مقابل نجاح محلي. جودة الاستعادة تشمل احترام حدودها، لا مجرد مشاهدة إشعار يصل في النهاية.