بروتوكول Web Push

لقد رأينا كيف يمكن استخدام مكتبة لتشغيل رسائل الدفع، ولكن ما الذي تفعله هذه المكتبات بالضبط؟

حسنًا، إنّها تقدّم طلبات للشبكة مع التأكّد من أنّ هذه الطلبات بالتنسيق الصحيح. المواصفة التي تحدِّد طلب الشبكة هذا هي Web Push Protocol.

مخطّط بياني لإرسال رسالة فورية من خادمك إلى
خدمة فورية

يوضح هذا القسم كيف يمكن للخادم التعرّف على نفسه باستخدام مفاتيح خادم التطبيق وكيف يتم إرسال البيانات الأساسية المشفّرة والبيانات المرتبطة بها.

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

مفاتيح خادم التطبيقات

عندما نشترك مستخدمًا، نمرر applicationServerKey. ويتم تمرير هذا المفتاح إلى خدمة الرسائل الفورية واستخدامه للتحقّق من أنّ التطبيق الذي اشترك فيه المستخدم هو أيضًا التطبيق الذي يُرسِل الرسائل الفورية.

عندما نقوم بتشغيل رسالة فورية، تكون هناك مجموعة من الرؤوس التي نرسلها والتي تسمح لخدمة الدفع بمصادقة التطبيق. (يتم تحديد ذلك بواسطة مواصفات VAPID.)

ماذا يعني كل هذا في الواقع وما الذي يحدث بالضبط؟ في ما يلي الخطوات التي يتم اتّخاذها لمحاولة مصادقة خادم التطبيقات:

  1. ويوقِّع خادم التطبيق بعض معلومات JSON باستخدام مفتاح التطبيق الخاص الخاص به.
  2. يتم إرسال هذه المعلومات الموقَّعة إلى خدمة الإرسال كعنوان في طلب POST.
  3. تستخدم خدمة Push المفتاح العام المخزَّن الذي استلمته من "pushManager.subscribe()" للتحقّق من أنّ المعلومات التي تم استلامها موقَّعة من خلال المفتاح الخاص المرتبط بالمفتاح العام. تذكَّر: المفتاح العام هو applicationServerKey الذي تم تمريره إلى طلب الاشتراك.
  4. إذا كانت المعلومات الموقَّعة صالحة، تُرسِل خدمة الدفع المدفوعة الرسائل المدفوعة إلى المستخدم.

فيما يلي مثال على تدفق المعلومات هذا. (يُرجى ملاحظة التوضيح في أسفل يمين الصفحة للإشارة إلى المفتاحَين العام والخاص).

صورة توضيحية لكيفية استخدام مفتاح خادم التطبيق الخاص عند إرسال رسالة

إنّ "المعلومات الموقَّعة" التي تتم إضافتها إلى عنوان في الطلب هي رمز JSON المميّز للويب.

رمز JSON المميّز للويب

رمز JSON المميّز للويب (أو JWT اختصارًا) هو طريقة ل إرسال رسالة إلى جهة خارجية كي يتمكّن المستلِم من التحقّق من هوية مُرسِلها.

عندما يتلقّى طرف ثالث رسالة، عليه الحصول على المفتاح العام للمُرسِل واستخدامه للتحقّق من صحة توقيع JWT. إذا كان التوقيع صالحًا، يجب أن يكون قد تم توقيع JWT باستخدام المفتاح الخاص المطابق، لذا يجب أن يكون من المُرسِل المتوقّع.

هناك مجموعة من المكتبات على https://jwt.io/ التي يمكنها إجراء التوقيع نيابةً عنك، وأنصحك بتنفيذ ذلك متى أمكن. لإكمال هذه المقالة، لنلقِ نظرة على كيفية إنشاء رمز JWT موقَّع يدويًا.

إرسال الويب بلغة البرمجة JWT الموقّعة

رمز JWT الموقّع هو مجرد سلسلة، على الرغم من أنه يمكن اعتباره ثلاث سلاسل مرتبطة بنقاط.

رسم توضيحي للسلاسل في رمز JSON المميّز للويب

السلسلتان الأولى والثانية (معلومات JWT وبيانات JWT) هما جزءان من ملف JSON تم تشفيرهما باستخدام ترميز base64، ما يعني أنّه يمكن قراءتهما بشكل علني.

السلسلة الأولى هي معلومات عن JWT نفسها، وتشير إلى الخوارزمية التي تم استخدامها لإنشاء التوقيع.

يجب أن تحتوي معلومات JWT للدفع على الويب على المعلومات التالية:

{
  "typ": "JWT",
  "alg": "ES256"
}

السلسلة الثانية هي بيانات JWT. يوفّر هذا الحقل معلومات عن مُرسِل رمز JWT وموضوعه ومدّة صلاحيته.

بالنسبة إلى الإشعارات الفورية على الويب، سيكون تنسيق البيانات على النحو التالي:

{
  "aud": "https://some-push-service.org",
  "exp": "1469618703",
  "sub": "mailto:example@web-push-book.org"
}

قيمة aud هي "الجمهور"، أي المستخدم الذي يخصّه معرّف JWT. بالنسبة إلى الإشعارات الفورية على الويب، يمثّل الجمهور خدمة الإشعارات الفورية، لذلك نضبطه على مصدر خدمة الإشعارات الفورية.

القيمة exp هي تاريخ انتهاء صلاحية رمز JWT، ويمنع ذلك المتلصصين من إعادة استخدام رمز JWT إذا اعترضوه. صلاحية الرمز عبارة عن طابع زمني بالثواني، ويجب ألا تتجاوز 24 ساعة.

في Node.js، يتم ضبط تاريخ انتهاء الصلاحية باستخدام:

Math.floor(Date.now() / 1000) + 12 * 60 * 60;

إنها 12 ساعة بدلاً من 24 ساعة لتجنب أي مشكلات في اختلافات الساعة بين تطبيق الإرسال وخدمة الإرسال.

أخيرًا، يجب أن تكون قيمة sub عنوان URL أو عنوان بريد إلكتروني mailto. ويعود السبب في ذلك إلى أنّه إذا أرادت خدمة الإرسال المباشر التواصل مع المُرسِل، يمكنها العثور على معلومات الاتصال من JWT. (لهذا السبب، كانت مكتبة Web-Push بحاجة إلى عنوان بريد إلكتروني).

تمامًا مثل معلومات JWT، يتم ترميز بيانات JWT كسلسلة base64 آمنة لعنوان URL.

السلسلة الثالثة، التوقيع، هي نتيجة الحصول على أول سلسلتين (معلومات JWT وبيانات JWT)، ودمجهما بحرف نقطة، والذي سنسميه "الرمز المميز غير الموقع"، وتوقيعه.

تتطلّب عملية التوقيع تشفير "الرمز المميّز غير الموقَّع" باستخدام ES256. وفقًا لمواصفات JWT ، ES256 هي اختصار لـ "ECDSA باستخدام منحنى P-256 و خوارزمية التجزئة SHA-256". باستخدام التشفير على الويب، يمكنك إنشاء التوقيع على النحو التالي:

// Utility function for UTF-8 encoding a string to an ArrayBuffer.
const utf8Encoder = new TextEncoder('utf-8');

// The unsigned token is the concatenation of the URL-safe base64 encoded
// header and body.
const unsignedToken = .....;

// Sign the |unsignedToken| using ES256 (SHA-256 over ECDSA).
const key = {
  kty: 'EC',
  crv: 'P-256',
  x: window.uint8ArrayToBase64Url(
    applicationServerKeys.publicKey.subarray(1, 33)),
  y: window.uint8ArrayToBase64Url(
    applicationServerKeys.publicKey.subarray(33, 65)),
  d: window.uint8ArrayToBase64Url(applicationServerKeys.privateKey),
};

// Sign the |unsignedToken| with the server's private key to generate
// the signature.
return crypto.subtle.importKey('jwk', key, {
  name: 'ECDSA', namedCurve: 'P-256',
}, true, ['sign'])
.then((key) => {
  return crypto.subtle.sign({
    name: 'ECDSA',
    hash: {
      name: 'SHA-256',
    },
  }, key, utf8Encoder.encode(unsignedToken));
})
.then((signature) => {
  console.log('Signature: ', signature);
});

يمكن لخدمة الدفع التحقق من صحة JWT باستخدام مفتاح خادم التطبيقات العامة لفك تشفير التوقيع والتأكد من أن السلسلة التي تم فك تشفيرها هي نفسها "الرمز المميز غير الموقع" (أي أول سلسلتين في JWT).

يتم إرسال ملف JWT الموقَّع (أي جميع السلاسل الثلاث التي تم ربطها بنقاط) إلى خدمة الدفع على الويب كعنوان Authorization مع إضافة WebPush في البداية، على النحو التالي:

Authorization: 'WebPush [JWT Info].[JWT Data].[Signature]';

ينصّ "بروتوكول Web Push" أيضًا على أنّه يجب إرسال مفتاح خادم التطبيقات العام في عنوان Crypto-Key كسلسلة بترميز base64 آمنة لعنوان URL مع prepended p256ecdsa=.

Crypto-Key: p256ecdsa=[URL Safe Base64 Public Application Server Key]

تشفير الحمولة

سنتعرّف بعد ذلك على كيفية إرسال حمولة بيانات باستخدام رسالة فورية، ومن ثمَّ عندما يتلقى تطبيق الويب رسالة فورية، يمكنه الوصول إلى البيانات التي يتلقّاها.

من الأسئلة الشائعة التي يطرحها أي مستخدم لخدمات دفع أخرى هي لماذا يجب تشفير حمولة الدفع على الويب؟ باستخدام التطبيقات الأصلية، يمكن أن تُرسِل الرسائل الفورية البيانات كنص عادي.

من مزايا إشعارات الويب الفورية أنّه لا يُشترط معرفة مقدّم خدمة الإشعارات الفورية، لأنّ جميع خدمات الإشعارات الفورية تستخدم واجهة برمجة التطبيقات نفسها (بروتوكول إشعارات الويب الفورية). يمكننا إرسال طلب بالتنسيق الصحيح والتوقع أن يتم إرسال رسالة إشعار فورية. الجانب السلبي من ذلك هو أنّه يمكن للمطوّرين إرسال الرسائل إلى خدمة دفع غير جديرة بالثقة. من خلال تشفير الحمولة، لا يمكن لخدمة الدفع قراءة البيانات المرسلة. ولا يمكن لأي جهة أخرى فك تشفير المعلومات سوى المتصفّح. ويؤدي ذلك إلى حماية data المستخدم.

يتم تحديد تشفير الحمولة في مواصفات تشفير الرسائل .

قبل الاطّلاع على الخطوات المحدّدة لتشفير الحمولة في الرسائل الفورية، علينا تغطية بعض الأساليب التي سيتم استخدامها أثناء عملية التشفير . (أودّ أن أشكر Mat Scales على مقالته الرائعة حول التشفير العميق.)

دالة ECDH ودالة HKDF

يتم استخدام كل من ECDH وHKDF طوال عملية التشفير ويقدّمان مزايا بهدف تشفير المعلومات.

ECDH: تبادل مفتاح Diffie-Hellman مع المنحنى الإهليلجي

تخيل أنّ هناك شخصين يريدان مشاركة المعلومات، وهما نبيلة وهبة. لدى نبيلة وسامر مفاتيحهما العامة والخاصة. تشارك نبيلة ويوسف مفتاحَيهما العامَّين مع بعضهما.

الخاصية المفيدة للمفاتيح التي تم إنشاؤها باستخدام ECDH هي أن نبيلة يمكنها استخدام مفتاحها الخاص ومفتاح يوسف العام لإنشاء قيمة سرية 'X'. يمكن لـ "بدر" إجراء الشيء نفسه، باستخدام مفتاحه الخاص ومفتاح "ألاء" العام لمحاولة إنشاء القيمة نفسها "س" بشكل مستقل. هذا يجعل "X" سرًا مشتركًا، ولم يكن على نبيلة وبوب سوى مشاركة مفتاحهما العام. يمكن الآن ليوسف ونبيلة استخدام "س" لتشفير الرسائل وفك تشفيرها بينهما.

على حد علمي، تحدد ECDH خصائص المنحنيات التي تسمح لهذه "الميزة" بإنشاء سر مشترك "X".

إليك شرحًا عامًا عن خوارزمية ECDH. إذا أردت معرفة المزيد من المعلومات، ننصحك بمشاهدة هذا الفيديو.

من حيث الرمز البرمجي، تأتي معظم اللغات أو المنصات مع مكتبات لتسهيل إنشاء هذه المفاتيح.

في العقدة، سننفّذ ما يلي:

const keyCurve = crypto.createECDH('prime256v1');
keyCurve.generateKeys();

const publicKey = keyCurve.getPublicKey();
const privateKey = keyCurve.getPrivateKey();

HKDF: دالة اشتقاق المفاتيح المستندة إلى HMAC

تقدّم ويكيبيديا وصفًا موجزًا لآلية HKDF:

دالة HKDF هي دالة اشتقاق مفاتيح تستند إلى دالة HMAC، وهي تعمل على تحويل أي مادة مفاتيح ضعيفة إلى مادة مفاتيح قوية من الناحية التشفيرية. ويمكن استخدامه، مثلاً، لتحويل الأسرار المشتركة التي تم تبادلها باستخدام خوارزمية Diffie Hellman إلى مادة مفاتيح مناسبة للاستخدام في التشفير أو التحقّق من السلامة أو المصادقة.

وبشكل أساسي، فإن HKDF ستأخذ مدخلات غير آمنة بشكل خاص وتجعلها أكثر أمانًا.

تتطلّب المواصفات التي تحدّد هذا التشفير استخدام SHA-256 كخوارزمية تجزئة ، ويجب ألا تزيد طول مفاتيح HKDF الناتجة في ميزة "الدفع على الويب" عن 256 بت (32 بايت).

في العقدة، يمكن تنفيذ ذلك على النحو التالي:

// Simplified HKDF, returning keys up to 32 bytes long
function hkdf(salt, ikm, info, length) {
  // Extract
  const keyHmac = crypto.createHmac('sha256', salt);
  keyHmac.update(ikm);
  const key = keyHmac.digest();

  // Expand
  const infoHmac = crypto.createHmac('sha256', key);
  infoHmac.update(info);

  // A one byte long buffer containing only 0x01
  const ONE_BUFFER = new Buffer(1).fill(1);
  infoHmac.update(ONE_BUFFER);

  return infoHmac.digest().slice(0, length);
}

أضِف نصيحة إلى مقالة Mat Scale لهذا المثال عن الرمز.

يتناول هذا القسم بشكل فضفاض ECDH وHKDF.

تقنية ECDH هي طريقة آمنة لمشاركة المفاتيح العامة وإنشاء مفتاح سرّي مشترك. وHKDF هو وسيلة للحصول على المواد غير الآمنة وجعلها آمنة.

سيتم استخدام ذلك أثناء تشفير حمولةنا. بعد ذلك، لنلقِ نظرة على ما نأخذه كข้อมูล مدخلة وكيفية تشفيرها.

مدخلات

عندما نريد إرسال رسالة فورية إلى مستخدم لديه حمولة، هناك ثلاثة إدخالات نحتاجها:

  1. الحمولة نفسها
  2. سر auth من PushSubscription.
  3. مفتاح p256dh من PushSubscription

لقد لاحظنا أنّ قيمتَي auth وp256dh يتم استردادهما من PushSubscription، ولكن للتذكير السريع، سنحتاج إلى القيم التالية نظرًا إلى الاشتراك:

subscription.toJSON().keys.auth;
subscription.toJSON().keys.p256dh;

subscription.getKey('auth');
subscription.getKey('p256dh');

يجب التعامل مع قيمة auth كقيمة سرية وألا تتم مشاركتها خارج التطبيق.

المفتاح p256dh هو مفتاح عام، ويُشار إليه أحيانًا باسم المفتاح العام للعميل. في ما يلي، سنشير إلى p256dh على أنّه المفتاح العام للاشتراك. ينشئ المتصفّح المفتاح العام للاشتراك. سيحافظ المتصفّح على سرية المفتاح الخاص وسيستخدمه لفك تشفير ملف التحميل.

يجب استخدام القيم الثلاث auth وp256dh وpayload كمدخلات، وستكون نتيجة عملية التشفير هي الحمولة المشفَّرة وقيمة الملح ومفتاح عام يُستخدَم فقط لتشفير البيانات.

الملح

يجب أن تكون القيمة العشوائية التي يتم استخدامها عبارة عن 16 بايت من البيانات العشوائية. في NodeJS، يمكننا إجراء ما يلي لإنشاء الملح:

const salt = crypto.randomBytes(16);

المفاتيح العامة / الخاصة

يجب إنشاء المفتاحَين العام والخاص باستخدام منحنى إهليجي من النوع P-256، ويمكنك إجراء ذلك في Node على النحو التالي:

const localKeysCurve = crypto.createECDH('prime256v1');
localKeysCurve.generateKeys();

const localPublicKey = localKeysCurve.getPublicKey();
const localPrivateKey = localKeysCurve.getPrivateKey();

سنشير إلى هذه المفاتيح باسم "المفاتيح المحلية". يتم استخدام هذه المفاتيح فقط للتشفير وليس لها أي تأثير مع مفاتيح خادم التطبيقات.

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

المفتاح السري المشترَك

الخطوة الأولى هي إنشاء مفتاح سري مشترَك باستخدام المفتاح العام للاشتراك والمفتاح الخاص الجديد (هل تتذكر شرح ECDH مع "منى" و"بدر"؟ تمامًا).

const sharedSecret = localKeysCurve.computeSecret(
  subscription.keys.p256dh,
  'base64',
);

ويُستخدَم ذلك في الخطوة التالية لاحتساب المفتاح شبه العشوائي (PRK).

مفتاح عشوائي زائف

مفتاح Pseudo Spam (PRK) هو مزيج من سر مصادقة اشتراك الدفع، بالإضافة إلى السر المشترك الذي أنشأناه للتو.

const authEncBuff = new Buffer('Content-Encoding: auth\0', 'utf8');
const prk = hkdf(subscription.keys.auth, sharedSecret, authEncBuff, 32);

قد تتساءل عن الغرض من السلسلة Content-Encoding: auth\0. باختصار، ليس له غرض واضح، على الرغم من أنّ المتصفّحات يمكنها فك تشفير رسالة واردة والبحث عن ترميز المحتوى المتوقّع. تُضيف القيمة \0 بايتًا بقيمة 0 إلى نهاية المخزن المؤقت. هذا متوقَّع من خلال المتصفّحات التي تفك تشفير الرسالة، ومن المتوقّع أن تكون هناك وحدات بايت متعددة لترميز المحتوى تليها بايت بالقيمة 0 تليها البيانات المشفَّرة.

يعمل المفتاح الزائف العشوائي الخاص بنا ببساطة على تشغيل المصادقة، والسر المشترك، وجزء من معلومات الترميز عبر HKDF (أي تعزيز قوة التشفير).

السياق

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

const keyLabel = new Buffer('P-256\0', 'utf8');

// Convert subscription public key into a buffer.
const subscriptionPubKey = new Buffer(subscription.keys.p256dh, 'base64');

const subscriptionPubKeyLength = new Uint8Array(2);
subscriptionPubKeyLength[0] = 0;
subscriptionPubKeyLength[1] = subscriptionPubKey.length;

const localPublicKeyLength = new Uint8Array(2);
subscriptionPubKeyLength[0] = 0;
subscriptionPubKeyLength[1] = localPublicKey.length;

const contextBuffer = Buffer.concat([
  keyLabel,
  subscriptionPubKeyLength.buffer,
  subscriptionPubKey,
  localPublicKeyLength.buffer,
  localPublicKey,
]);

ويكون مخزن السياق النهائي عبارة عن تصنيف وعدد وحدات البايت في المفتاح العام للاشتراك، يليه المفتاح نفسه، ثم عدد وحدات البايت للمفتاح العام المحلي، يليه المفتاح نفسه.

باستخدام قيمة السياق هذه، يمكننا استخدامها في إنشاء مفتاح تشفير متغيّر ومفتاح تشفير محتوى (CEK).

مفتاح تشفير المحتوى والمفتاح العشوائي

المفتاح المؤقت هو قيمة تمنع هجمات إعادة التسجيل، لأنّه يجب استخدامه مرة واحدة فقط.

مفتاح تشفير المحتوى (CEK) هو المفتاح الذي سيُستخدم في النهاية لتشفير حمولةنا.

نحتاج أولاً إلى إنشاء وحدات بايت من البيانات لـ nonce وCEK، وهي ببساطة سلسلة ترميز محتوى متبوعة بالمخزن المؤقت للسياق الذي حسبناه للتو:

const nonceEncBuffer = new Buffer('Content-Encoding: nonce\0', 'utf8');
const nonceInfo = Buffer.concat([nonceEncBuffer, contextBuffer]);

const cekEncBuffer = new Buffer('Content-Encoding: aesgcm\0');
const cekInfo = Buffer.concat([cekEncBuffer, contextBuffer]);

يتم تشغيل هذه المعلومات من خلال HKDF مع دمج الملح وPRK مع nonceInfo وcekInfo:

// The nonce should be 12 bytes long
const nonce = hkdf(salt, prk, nonceInfo, 12);

// The CEK should be 16 bytes long
const contentEncryptionKey = hkdf(salt, prk, cekInfo, 16);

نحصل على مفتاح التشفير الخاص بنا ومفتاح تشفير المحتوى.

تنفيذ التشفير

الآن بعد أن حصلنا على مفتاح تشفير المحتوى، يمكننا تشفير الحمولة.

ننشئ مفتاح تشفير AES128 باستخدام مفتاح تشفير المحتوى باعتباره المفتاح ويكون المفتاح العشوائي هو متجه الإعداد.

في Node، يتم ذلك على النحو التالي:

const cipher = crypto.createCipheriv(
  'id-aes128-GCM',
  contentEncryptionKey,
  nonce,
);

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

يجب إضافة بايتين من الحشو للإشارة إلى طول أي حشو إضافي.

على سبيل المثال، إذا لم تُضِف أيّ بيانات تمديد، سيكون لديك بايتان بالقيمة 0، أي أنّه لا تتوفّر بيانات تمديد، وبعد هاتين البايتَين، ستقرأ الحمولة. إذا أضفت 5 بايت من الحشو، ستكون قيمة أول بايتين 5، لذلك سيقرأ المستهلك بعد ذلك خمسة بايت إضافية ثم يبدأ بقراءة الحمولة.

const padding = new Buffer(2 + paddingLength);
// The buffer must be only zeros, except the length
padding.fill(0);
padding.writeUInt16BE(paddingLength, 0);

بعد ذلك، نُجري عملية التشفير على الحشو والحمولة.

const result = cipher.update(Buffer.concat(padding, payload));
cipher.final();

// Append the auth tag to the result -
// https://nodejs.org/api/crypto.html#crypto_cipher_getauthtag
const encryptedPayload = Buffer.concat([result, cipher.getAuthTag()]);

لدينا الآن الحمولة المشفّرة. رائع

كل ما تبقى هو تحديد كيفية إرسال الحمولة إلى خدمة الإرسال الفوري.

رؤوس الحمولة المشفَّرة ونصها

لإرسال هذه الحمولة المشفرة إلى خدمة الإرسال، نحتاج إلى تعريف بعض العناوين المختلفة في طلب POST.

عنوان التشفير

يجب أن يحتوي عنوان "التشفير" على القيمة الملحية المستخدمة لتشفير الحمولة.

ويجب أن تكون البيانات العشوائية العشوائية بسعة 16 بايت بترميز آمن لعنوان URL الأساسي بترميز base64 وأن تُضيف إلى عنوان التشفير، كما يلي:

Encryption: salt=[URL Safe Base64 Encoded Salt]

عنوان مفتاح التشفير

تبيّن لنا أنّه يتم استخدام العنوان Crypto-Key ضمن القسم "مفاتيح خادم التطبيقات" لاحتواء مفتاح خادم التطبيقات العام.

يُستخدم هذا العنوان أيضًا لمشاركة المفتاح العام المحلي المُستخدَم لتشفير الحمولة.

يظهر العنوان الناتج على النحو التالي:

Crypto-Key: dh=[URL Safe Base64 Encoded Local Public Key String]; p256ecdsa=[URL Safe Base64 Encoded Public Application Server Key]

نوع المحتوى وطوله وعناوين الترميز

يشير رأس Content-Length إلى عدد وحدات البايت في الحمولة المشفّرة . إنّ عنوانَي Content-Type وContent-Encoding هما قيم ثابتة. يظهر ذلك أدناه.

Content-Length: [Number of Bytes in Encrypted Payload]
Content-Type: 'application/octet-stream'
Content-Encoding: 'aesgcm'

بعد ضبط هذه العناوين، علينا إرسال الحمولة المشفّرة كنص الطلب. لاحظ أنّه تم ضبط Content-Type على application/octet-stream. ويرجع ذلك إلى أنّه يجب إرسال الحمولة المشفَّرة كبث بايتات.

في NodeJS، نفعل ذلك على النحو التالي:

const pushRequest = https.request(httpsOptions, function(pushResponse) {
pushRequest.write(encryptedPayload);
pushRequest.end();

هل هناك المزيد من العناوين؟

لقد تناولنا العناوين المستخدمة في مفاتيح JWT / خادم التطبيقات (أي كيفية التعرّف على التطبيق من خلال خدمة الإرسال) وتناولنا العناوين المستخدمة لإرسال حمولة مشفرة.

هناك رؤوس إضافية تستخدمها خدمات الدفع لتغيير سلوك الرسائل المُرسَلة. بعض هذه العناوين مطلوبة، في حين أن البعض الآخر اختياري.

عنوان مدة البقاء (TTL)

مطلوب

TTL (أو مدة البقاء) هو عدد صحيح يحدّد عدد الثواني التي تريد أن تبقى فيها رسالة الإشعار الفوري متوفّرة على خدمة الإشعارات الفورية قبل إرسالها. عند انتهاء صلاحية TTL، ستتم إزالة الرسالة من قائمة انتظار خدمة الدفع ولن يتم تسليمها.

TTL: [Time to live in seconds]

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

من الناحية الفنية، يمكن لخدمة الدفع أن تقلّل TTL من رسالة الدفع إذا أرادت ذلك. يمكنك معرفة ما إذا حدث ذلك من خلال فحص العنوان TTL في الاستجابة الواردة من خدمة الإرسال الفوري.

الموضوع

اختياريّ

المواضيع هي سلاسل يمكن استخدامها لاستبدال رسائل في انتظار المراجعة برسالة جديدة إذا كانت تحمل أسماء مواضيع متطابقة.

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

حاجة ماسة

اختياريّ

تشير حالة "العاجلة" إلى خدمة الإشعارات الفورية بمدى أهمية الرسالة بالنسبة إلى المستخدم. يمكن أن تستخدم خدمة الإشعارات الفورية هذه الميزة للمساعدة في الحفاظ على عمر بطارية جهاز المستخدم من خلال تنشيطها فقط لتلقّي الرسائل المهمة عندما تكون البطارية منخفضة.

يتم تحديد قيمة العنوان كما هو موضّح أدناه. القيمة التلقائية هي normal.

Urgency: [very-low | low | normal | high]

كل العناصر معًا

إذا كانت لديك أسئلة أخرى حول آلية عمل هذه الميزة، يمكنك في أي وقت الاطّلاع على كيفية بدء المكتبات لرسائل الدفع على web-push-libs org.

بعد الحصول على حمولة مشفَّرة والعناوين أعلاه، ما عليك سوى إرسال طلب POST إلى endpoint في PushSubscription.

ما الذي سنفعله بالردّ على طلب POST هذا؟

رد من خدمة الدفع

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

رمز الحالة الوصف
201 تمّ إنشاء المحتوى. تم استلام طلب إرسال رسالة فورية وقبوله.
429 عدد الطلبات كبير جدًا. وهذا يعني أنّ خادم التطبيقات قد بلغ الحدّ الأقصى لمعدل الإرسال في خدمة الإشعارات الفورية. يجب أن تحتوي خدمة الدفع على العنوان "إعادة المحاولة بعد" للإشارة إلى المدة المستغرَقة قبل إجراء طلب آخر.
400 الطلب غير صالح. يعني ذلك بشكل عام أنّ أحد العناوين غير صالح أو أنّ تنسيقه غير صحيح.
404 غير موجودة يشير ذلك إلى أنّ الاشتراك انتهت صلاحيته ولا يمكن استخدامه. في هذه الحالة، عليك حذف PushSubscription والانتظار إلى أن يعيد العميل اشتراك المستخدم.
410 تمّت إزالة المحتوى. لم يعُد الاشتراك صالحًا ويجب إزالته من خادم التطبيقات. يمكن إعادة إنتاج هذا الخطأ من خلال استدعاء `unsubscribe()` على `PushSubscription`.
413 حجم الحمولة كبير جدًا. الحد الأدنى لحجم الحمولة التي يجب أن تتوافق معها خدمة الدفع هو 4096 بايت (أو 4 كيلوبايت).

يمكنك أيضًا الاطّلاع على معيار Web Push (RFC8030) للحصول على مزيد من المعلومات حول رموز حالة HTTP.

الخطوات التالية

الدروس التطبيقية حول الترميز