بروتوكول Web Push

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

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

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

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

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

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

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

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

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

  1. يوقِّع خادم التطبيق بعض معلومات JSON باستخدام مفتاح التطبيق الخاص.
  2. يتم إرسال هذه المعلومات الموقَّعة إلى خدمة الدفع كعنوان في طلب POST.
  3. تستخدم خدمة الدفع المفتاح العام المخزّن الذي تلقّته من "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 والمرسل إليه ومدة صلاحيته.

بالنسبة إلى Web Push، سيكون للبيانات بالتنسيق التالي:

{
  "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 الموقَّع (أي جميع السلاسل الثلاث المرتبطة بنقاط) إلى خدمة Web Push باعتبارها عنوان Authorization مع إضافة WebPush في البداية، على النحو التالي:

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

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

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

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

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

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

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

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

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

ECDH وHKDF

يُستخدم كل من ECDH وHKDF خلال عملية التشفير وهما يوفران مزايا لغرض تشفير المعلومات.

ECDH: تبادل المفاتيح "ديفي-هيلمان" Elliptic Curve

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

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

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

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

في ما يتعلق بالرموز البرمجية، تأتي معظم اللغات / المنصات مزوَّدة بمكتبات لتسهيل إنشاء هذه المفاتيح.

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

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

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

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

تقدّم موسوعة ويكيبيديا وصفًا موجزًا حول دولار هونغ كونغ:

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

وفي الأساس، سيأخذ 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).

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

المفتاح الزائف (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,
]);

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

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

مفتاح تشفير المحتوى والنص الأساسي

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

مفتاح تشفير المحتوى (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);

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

إجراء التشفير

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

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

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

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

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

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

على سبيل المثال، إذا لم تضِف أي مساحة متروكة، فسيكون لديك وحدتا بايت بالقيمة 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 هذا؟

استجابة من خدمة Push

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

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

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

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