Kemenangan Interoperabilitas Web Push

Joe Medley
Joe Medley

Saat pertama kali mendukung Web Push API, Chrome mengandalkan layanan push Firebase Cloud Messaging (FCM), yang sebelumnya dikenal sebagai Google Cloud Messaging (GCM). Hal ini diperlukan menggunakan API eksklusifnya. Hal ini memungkinkan Chrome menyediakan Web Push API bagi developer saat spesifikasi Web Push Protocol masih ditulis dan kemudian menyediakan autentikasi (artinya pengirim pesan sesuai dengan yang mereka nyatakan) pada saat Web Push Protocol tidak memilikinya. Kabar baik: tidak satu pun dari pernyataan tersebut benar.

FCM / GCM dan Chrome kini mendukung Web Push Protocol standar, sedangkan autentikasi pengirim dapat dilakukan dengan menerapkan VAPID, yang berarti aplikasi web Anda tidak lagi memerlukan 'alat_pengirim_id'.

Dalam artikel ini, saya akan menjelaskan cara mengonversi kode server yang ada untuk menggunakan Web Push Protocol dengan FCM. Selanjutnya, saya akan menunjukkan cara menerapkan VAPID di kode klien dan server.

FCM mendukung Web Push Protocol

Mari kita mulai dengan sedikit konteks. Ketika aplikasi web Anda mendaftar untuk langganan push, aplikasi web akan diberi URL layanan push. Server Anda akan menggunakan endpoint ini untuk mengirim data ke pengguna melalui aplikasi web. Di Chrome, Anda akan mendapatkan endpoint FCM jika berlangganan pengguna tanpa VAPID. (Kami akan membahas VAPID nanti). Sebelum FCM mendukung Web Push Protocol, Anda harus mengekstrak ID pendaftaran FCM dari akhir URL dan memasukkannya ke header sebelum membuat permintaan FCM API. Misalnya, endpoint FCM https://android.googleapis.com/gcm/send/ABCD1234 akan memiliki ID pendaftaran 'ABCD1234'.

Setelah FCM mendukung Web Push Protocol, Anda dapat membiarkan endpoint tetap utuh dan menggunakan URL sebagai endpoint Web Push Protocol. (Ini membuatnya selaras dengan Firefox dan mudah-mudahan setiap browser lainnya di masa mendatang.)

Sebelum mempelajari VAPID lebih lanjut, kami perlu memastikan bahwa kode server menangani endpoint FCM dengan benar. Di bawah ini adalah contoh pembuatan permintaan ke layanan push di Node. Perhatikan bahwa untuk FCM, kita menambahkan kunci API ke header permintaan. Untuk endpoint layanan push lainnya, hal ini tidak diperlukan. Untuk Chrome sebelum versi 52, Opera Android, dan Browser Samsung, Anda juga masih harus menyertakan 'hreflang_navigation_id' dalam manifest.json aplikasi web Anda. Kunci API dan ID pengirim digunakan untuk memeriksa apakah server yang membuat permintaan benar-benar diizinkan untuk mengirim pesan ke pengguna penerima.

const headers = new Headers();
// 12-hour notification time to live.
headers.append('TTL', 12 * 60 * 60);
// Assuming no data is going to be sent
headers.append('Content-Length', 0);

// Assuming you're not using VAPID (read on), this
// proprietary header is needed
if(subscription.endpoint
    .indexOf('https://android.googleapis.com/gcm/send/') === 0) {
    headers.append('Authorization', 'GCM_API_KEY');
}

fetch(subscription.endpoint, {
    method: 'POST',
    headers: headers
})
.then(response => {
    if (response.status !== 201) {
    throw new Error('Unable to send push message');
    }
});

Ingat, ini adalah perubahan pada API FCM / GCM, sehingga Anda tidak perlu mengupdate langganan. Cukup ubah kode server untuk menentukan header seperti yang ditunjukkan di atas.

Memperkenalkan VAPID untuk identifikasi server

VAPID adalah nama pendek baru yang keren untuk "Voluntary Application Server Identification". Spesifikasi baru ini pada dasarnya menentukan handshake antara server aplikasi Anda dan layanan push serta memungkinkan layanan push untuk mengonfirmasi situs mana yang mengirim pesan. Dengan VAPID, Anda dapat menghindari langkah-langkah khusus FCM untuk mengirim pesan push. Anda tidak lagi memerlukan project Firebase, gcm_sender_id, atau header Authorization.

Prosesnya cukup sederhana:

  1. Server aplikasi Anda membuat pasangan kunci publik/pribadi. Kunci publik diberikan ke aplikasi web Anda.
  2. Saat pengguna memilih untuk menerima push, tambahkan kunci publik ke objek opsi panggilan subscribe().
  3. Saat server aplikasi Anda mengirim pesan push, sertakan Token Web JSON yang ditandatangani bersama dengan kunci publik.

Mari kita lihat langkah-langkah ini secara detail.

Membuat pasangan kunci publik/pribadi

Saya sangat buruk dalam hal enkripsi, jadi inilah bagian yang relevan dari spesifikasi mengenai format kunci publik/pribadi VAPID:

Server aplikasi HARUS membuat dan mempertahankan pasangan kunci penandatanganan yang dapat digunakan dengan elliptic curve digital signature (ECDSA) pada kurva P-256.

Anda dapat melihat cara melakukannya di library node web-push:

function generateVAPIDKeys() {
    var curve = crypto.createECDH('prime256v1');
    curve.generateKeys();

    return {
    publicKey: curve.getPublicKey(),
    privateKey: curve.getPrivateKey(),
    };
}

Berlangganan dengan kunci publik

Agar pengguna Chrome berlangganan push dengan kunci publik VAPID, Anda harus meneruskan kunci publik sebagai Uint8Array menggunakan parameter applicationServerKey dari metode subscribe().

const publicKey = new Uint8Array([0x4, 0x37, 0x77, 0xfe, …. ]);
serviceWorkerRegistration.pushManager.subscribe(
    {
    userVisibleOnly: true,
    applicationServerKey: publicKey
    }
);

Anda akan mengetahui apakah metode tersebut telah berfungsi dengan memeriksa endpoint di objek langganan yang dihasilkan. Jika origin adalah fcm.googleapis.com, fungsi tersebut berfungsi.

https://fcm.googleapis.com/fcm/send/ABCD1234

Mengirim pesan push

Untuk mengirim pesan menggunakan VAPID, Anda harus membuat permintaan Web Push Protocol normal dengan dua header HTTP tambahan: header Authorization dan header Crypto-Key.

Header otorisasi

Header Authorization adalah JSON Web Token (JWT) yang ditandatangani dengan 'WebPush ' di depannya.

JWT adalah cara untuk berbagi objek JSON dengan pihak kedua sedemikian rupa sehingga pihak pengirim dapat menandatanganinya dan pihak penerima dapat memverifikasi tanda tangan tersebut dari pengirim yang diharapkan. Struktur JWT adalah tiga {i>string<i} terenkripsi, digabungkan dengan satu titik di antaranya.

<JWTHeader>.<Payload>.<Signature>

Header JWT

Header JWT berisi nama algoritma yang digunakan untuk penandatanganan dan jenis token. Untuk VAPID, kodenya harus berupa:

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

URL ini kemudian dienkode ke URL base64 dan membentuk bagian pertama dari JWT.

Payload

Payload adalah objek JSON lain yang berisi hal berikut:

  • Audiens ("aud")
    • Ini adalah asal layanan push (BUKAN asal situs Anda). Di JavaScript, Anda dapat melakukan hal berikut untuk mendapatkan audiens: const audience = new URL(subscription.endpoint).origin
  • Waktu Habis Masa Berlaku ("exp")
    • Ini adalah jumlah detik hingga permintaan dianggap telah habis masa berlakunya. Waktu ini HARUS dalam waktu 24 jam sejak permintaan dibuat, dalam UTC.
  • Subjek ("sub")
    • Subjek harus berupa URL atau URL mailto:. Hal ini menyediakan kontak jika layanan push perlu menghubungi pengirim pesan.

Contoh payload dapat terlihat seperti berikut:

{
    "aud": "http://push-service.example.com",
    "exp": Math.floor((Date.now() / 1000) + (12 * 60 * 60)),
    "sub": "mailto: my-email@some-url.com"
}

Objek JSON ini dienkode dengan URL base64 dan membentuk bagian kedua dari JWT.

Tanda tangan

Tanda tangan ini adalah hasil dari penggabungan header dan payload yang dienkode dengan titik, lalu mengenkripsi hasilnya menggunakan kunci pribadi VAPID yang Anda buat sebelumnya. Hasilnya itu sendiri harus ditambahkan ke {i>header<i} dengan titik.

Saya tidak akan menampilkan contoh kode untuk ini karena ada sejumlah library yang akan mengambil objek JSON header dan payload dan menghasilkan tanda tangan ini untuk Anda.

JWT yang ditandatangani digunakan sebagai header Otorisasi dengan tambahan 'WebPush ' dan akan terlihat seperti berikut:

WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTQ2NjY2ODU5NCwic3ViIjoibWFpbHRvOnNpbXBsZS1wdXNoLWRlbW9AZ2F1bnRmYWNlLmNvLnVrIn0.Ec0VR8dtf5qb8Fb5Wk91br-evfho9sZT6jBRuQwxVMFyK5S8bhOjk8kuxvilLqTBmDXJM5l3uVrVOQirSsjq0A

Perhatikan beberapa hal tentang ini. Pertama, header Authorization secara harfiah berisi kata 'WebPush' dan harus diikuti spasi kemudian JWT. Perhatikan juga titik-titik yang memisahkan header, payload, dan tanda tangan JWT.

Header Crypto-Key

Selain header Authorization, Anda harus menambahkan kunci publik VAPID ke header Crypto-Key sebagai string yang dienkode URL base64 dengan p256ecdsa= yang ditambahkan di atasnya.

p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo

Saat mengirim notifikasi dengan data terenkripsi, Anda sudah menggunakan header Crypto-Key. Jadi, untuk menambahkan kunci server aplikasi, Anda hanya perlu menambahkan titik koma sebelum menambahkan konten di atas, sehingga menghasilkan:

dh=BGEw2wsHgLwzerjvnMTkbKrFRxdmwJ5S_k7zi7A1coR_sVjHmGrlvzYpAT1n4NPbioFlQkIrTNL8EH4V3ZZ4vJE;
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaN

Realitas perubahan ini

Dengan VAPID, Anda tidak perlu lagi mendaftar ke akun GCM untuk menggunakan push di Chrome, dan Anda dapat menggunakan jalur kode yang sama untuk membuat pengguna berlangganan dan mengirim pesan ke pengguna di Chrome dan Firefox. Keduanya mengikuti standar.

Perlu diingat bahwa di Chrome 51 dan versi sebelumnya, Opera untuk Android dan browser Samsung, Anda masih harus menentukan gcm_sender_id dalam manifes aplikasi web dan Anda harus menambahkan header Otorisasi ke endpoint FCM yang akan ditampilkan.

VAPID menyediakan batasan lain dari persyaratan eksklusif ini. Jika Anda menerapkan VAPID, kode ini akan berfungsi di semua browser yang mendukung web push. Karena makin banyak browser yang mendukung VAPID, Anda dapat memutuskan kapan harus menghapus gcm_sender_id dari manifes Anda.