Kemenangan Interoperabilitas Web Push

Joe Medley
Joe Medley

Saat pertama kali mendukung Web Push API, Chrome mengandalkan Firebase Cloud Messaging (FCM),yang sebelumnya dikenal sebagai Google Cloud Messaging (GCM), layanan push. Tindakan ini memerlukan penggunaan API eksklusif. Hal ini memungkinkan Chrome menyediakan Web Push API bagi developer saat spesifikasi Web Push Protocol masih ditulis, lalu memberikan autentikasi (artinya pengirim pesan adalah orang yang mereka nyatakan) pada saat Web Push API tidak memilikinya. Kabar baik: tidak ada lagi yang benar.

FCM / GCM dan Chrome kini mendukung Web Push Protocol standar, sementara autentikasi pengirim dapat dicapai dengan mengimplementasikan VAPID. Artinya, aplikasi web Anda tidak lagi memerlukan 'gcm_sender_id'.

Dalam artikel ini, pertama-tama 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 Anda.

FCM mendukung Protokol Push Web

Mari kita mulai dengan sedikit konteks. Saat aplikasi web Anda mendaftar ke langganan push, aplikasi web akan diberi URL layanan push. Server Anda akan menggunakan endpoint ini untuk mengirim data ke pengguna melalui aplikasi web Anda. Di Chrome, Anda akan diberi endpoint FCM jika Anda berlangganan pengguna tanpa VAPID. (Kita akan membahas VAPID nanti). Sebelum FCM mendukung Web Push Protocol, Anda harus mengekstrak ID pendaftaran FCM dari akhir URL dan memasukkannya di 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. (Hal ini selaras dengan Firefox dan semoga setiap browser lainnya di masa mendatang.)

Sebelum mempelajari VAPID, kami perlu memastikan bahwa kode server kami menangani endpoint FCM dengan benar. Di bawah ini adalah contoh pembuatan permintaan ke layanan push di Node. Perhatikan bahwa untuk FCM, kami 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 tetap harus menyertakan 'gcm_sender_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');
    }
});

Perlu diingat, ini adalah perubahan pada API FCM / GCM, sehingga Anda tidak perlu mengupdate langganan. Cukup ubah kode server untuk menentukan header seperti yang ditampilkan 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 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 beserta kunci publik.

Mari kita lihat langkah-langkah ini secara mendetail.

Membuat pasangan kunci publik/pribadi

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

Server aplikasi HARUS menghasilkan dan mempertahankan pasangan kunci penandatanganan yang dapat digunakan dengan elliptic curve digital signature (ECDSA) melalui 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 endpoint berhasil dengan memeriksa endpoint di objek langganan yang dihasilkan. Jika originnya fcm.googleapis.com, endpoint 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 Token Web JSON (JWT) yang ditandatangani dengan 'WebPush ' di depannya.

JWT adalah cara berbagi objek JSON dengan pihak kedua sedemikian rupa sehingga pihak pengirim dapat menandatanganinya dan pihak penerima dapat memverifikasi bahwa tanda tangan tersebut berasal dari pengirim yang diharapkan. Struktur JWT adalah tiga string terenkripsi yang 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, parameternya harus:

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

Ini kemudian dienkode menggunakan URL base64 dan membentuk bagian pertama 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 harus dianggap berakhir masa berlakunya. Hal ini HARUS dalam waktu 24 jam sejak permintaan dibuat, dalam UTC.
  • Subjek ("sub")
    • Subjek harus berupa URL atau URL mailto:. 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 tersebut dihasilkan dari penggabungan header dan payload yang dienkode dengan sebuah titik, lalu hasilnya dienkripsi menggunakan kunci pribadi VAPID yang Anda buat sebelumnya. Hasilnya harus ditambahkan ke header dengan titik.

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

JWT yang ditandatangani digunakan sebagai header Authorization dengan 'WebPush ' di depannya 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 setelah JWT. Perhatikan juga titik-titik yang memisahkan header JWT, payload, dan tanda tangan.

Header Crypto-Key

Selain header Authorization, Anda juga harus menambahkan kunci publik VAPID ke header Crypto-Key sebagai string berenkode URL base64 dengan p256ecdsa= diawalinya.

p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo

Saat Anda mengirim notifikasi dengan data terenkripsi, Anda sudah menggunakan header Crypto-Key sehingga 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 akun ke GCM untuk menggunakan push di Chrome, dan Anda dapat menggunakan jalur kode yang sama untuk membuat pengguna berlangganan serta mengirim pesan ke pengguna di Chrome dan Firefox. Keduanya mengikuti standar.

Yang perlu diingat adalah bahwa di Chrome 51 dan yang 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 memberikan kemudahan dari persyaratan eksklusif ini. Jika Anda mengimplementasikan VAPID, VAPID akan berfungsi di semua browser yang mendukung web push. Karena semakin banyak browser yang mendukung VAPID, Anda dapat memutuskan kapan harus menghapus gcm_sender_id dari manifes Anda.