ในช่วงแรก Chrome รองรับ Web Push API โดยใช้บริการพุชของ Firebase Cloud Messaging (FCM) ซึ่งก่อนหน้านี้เรียกว่าการรับส่งข้อความในระบบคลาวด์ของ Google (GCM) ซึ่งต้องใช้ API ที่เป็นกรรมสิทธิ์ของตนเอง การทำเช่นนี้ช่วยให้ Chrome ทำให้นักพัฒนาซอฟต์แวร์สามารถใช้ Web Push API ได้ในเวลาที่ยังเขียนข้อกำหนดของโปรโตคอล Web Push อยู่และให้มีการตรวจสอบสิทธิ์ในภายหลัง (หมายความว่าผู้ส่งข้อความเป็นบุคคลจริงตามที่อ้าง) ในช่วงเวลาที่โปรโตคอล Web Push ไม่มีโปรโตคอลดังกล่าว ข่าวดีก็คือทั้ง 2 อย่างนี้ไม่เป็นความจริงอีกต่อไป
FCM / GCM และ Chrome รองรับโปรโตคอล Web Push มาตรฐานแล้ว ในขณะที่การตรวจสอบสิทธิ์ผู้ส่งสามารถทำได้โดยใช้ VAPID ซึ่งหมายความว่าเว็บแอปไม่จำเป็นต้องใช้ "gcm_sender_id" อีกต่อไป
ในบทความนี้ ผมจะอธิบายวิธีแปลงโค้ดเซิร์ฟเวอร์ที่มีอยู่เพื่อใช้โปรโตคอล Web Push กับ FCM ก่อน ต่อไปผมจะแสดงวิธีใช้ VAPID ทั้งในโค้ดของไคลเอ็นต์และโค้ดเซิร์ฟเวอร์
FCM รองรับ Web Push Protocol
เรามาเริ่มที่บริบทกันสักเล็กน้อย เมื่อเว็บแอปพลิเคชันลงทะเบียนสำหรับการสมัครใช้บริการพุชจะได้รับ URL ของบริการพุช เซิร์ฟเวอร์จะใช้ปลายทางนี้เพื่อส่งข้อมูลไปยังผู้ใช้ผ่านเว็บแอป คุณจะได้รับปลายทาง FCM ใน Chrome หากสมัครใช้บริการผู้ใช้ที่ไม่มี VAPID (เราจะพูดถึง VAPID
ในภายหลัง) ก่อนที่ FCM จะรองรับ Web Push Protocol คุณจะต้องดึงรหัสการลงทะเบียน FCM จากส่วนท้ายของ URL และใส่รหัสไว้ในส่วนหัวก่อนส่งคำขอ API ของ FCM เช่น ปลายทาง FCM ของ https://android.googleapis.com/gcm/send/ABCD1234
จะมีรหัสการลงทะเบียน "ABCD1234"
ตอนนี้ FCM รองรับ Web Push Protocol แล้ว คุณจึงปล่อยปลายทางไว้ตามเดิมและใช้ URL เป็นปลายทาง Web Push Protocol ได้ (ซึ่งจะทำให้สอดคล้องกับ Firefox และหวังว่าเบราว์เซอร์อื่นๆ ในอนาคต)
ก่อนจะเจาะลึกเรื่อง VAPID เราต้องตรวจสอบว่าโค้ดเซิร์ฟเวอร์ของเราจัดการกับปลายทาง FCM ได้อย่างถูกต้อง ด้านล่างคือตัวอย่างการส่งคำขอไปยังบริการพุชในโหนด สำหรับ FCM เราจะเพิ่มคีย์ API ในส่วนหัวของคำขอ ส่วนปลายทางบริการพุชอื่นๆ จะไม่จำเป็น สำหรับ Chrome เวอร์ชันก่อน 52, Opera Android และเบราว์เซอร์ Samsung คุณยังคงต้องใส่ "gcm_sender_id" ใน manifest.json ของเว็บแอป คีย์ API และรหัสผู้ส่งใช้เพื่อตรวจสอบว่าเซิร์ฟเวอร์ที่ส่งคำขอได้รับอนุญาตให้ส่งข้อความถึงผู้ใช้ที่เป็นผู้รับหรือไม่
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');
}
});
โปรดทราบว่านี่เป็นการเปลี่ยนแปลงใน FCM / API ของ GCM คุณจึงไม่จำเป็นต้องอัปเดตการสมัครรับข้อมูล เพียงแค่เปลี่ยนรหัสเซิร์ฟเวอร์เพื่อกำหนดส่วนหัวดังที่แสดงด้านบน
ขอแนะนำ VAPID สำหรับการระบุเซิร์ฟเวอร์
VAPID เป็นชื่อย่อใหม่ที่น่าสนใจสำหรับ
"รหัสเซิร์ฟเวอร์แอปพลิเคชันโดยสมัครใจ" ข้อกำหนดใหม่นี้จะกำหนดแฮนด์เชคระหว่างเซิร์ฟเวอร์แอปและบริการพุชเป็นหลัก และจะทำให้บริการพุชสามารถยืนยันได้ว่าเว็บไซต์ใดกำลังส่งข้อความอยู่
เมื่อใช้ VAPID คุณจะหลีกเลี่ยงขั้นตอนเฉพาะสำหรับ FCM ในการส่งข้อความพุชได้ คุณไม่จำเป็นต้องใช้โปรเจ็กต์ Firebase, gcm_sender_id
หรือส่วนหัว Authorization
อีกต่อไป
กระบวนการนี้ค่อนข้างง่าย
- แอปพลิเคชันเซิร์ฟเวอร์จะสร้างคู่คีย์สาธารณะ/ส่วนตัว เว็บแอปจะได้รับ คีย์สาธารณะ
- เมื่อผู้ใช้เลือกรับการพุช ให้เพิ่มคีย์สาธารณะไปยังออบเจ็กต์ตัวเลือกของการเรียกใช้ subscription()
- เมื่อเซิร์ฟเวอร์แอปส่งข้อความพุช ให้ใส่ JSON Web Token ที่ลงนามไว้พร้อมกับคีย์สาธารณะ
มาดูรายละเอียดของขั้นตอนเหล่านี้กันเลย
สร้างคู่คีย์สาธารณะ/ส่วนตัว
เราหละหลวมเรื่องการเข้ารหัส ดังนั้นมาดูส่วนที่เกี่ยวข้องจากข้อกำหนดเกี่ยวกับรูปแบบของคีย์สาธารณะ/คีย์ส่วนตัว VAPID กัน
เซิร์ฟเวอร์แอปพลิเคชันควรสร้างและดูแลรักษาคู่คีย์ Signing ที่ใช้ได้กับลายเซ็นดิจิทัล Elliptic Curve (ECDSA) เหนือเส้นโค้ง P-256
ดูวิธีดำเนินการดังกล่าวได้ในไลบรารีโหนด Web-push
function generateVAPIDKeys() {
var curve = crypto.createECDH('prime256v1');
curve.generateKeys();
return {
publicKey: curve.getPublicKey(),
privateKey: curve.getPrivateKey(),
};
}
การสมัครใช้บริการด้วยคีย์สาธารณะ
หากต้องการสมัครใช้บริการผู้ใช้ Chrome เพื่อพุชด้วยคีย์สาธารณะ VAPID คุณต้องส่งคีย์สาธารณะเป็น Uint8Array โดยใช้พารามิเตอร์ applicationServerKey
ของเมธอด subscription()
const publicKey = new Uint8Array([0x4, 0x37, 0x77, 0xfe, …. ]);
serviceWorkerRegistration.pushManager.subscribe(
{
userVisibleOnly: true,
applicationServerKey: publicKey
}
);
คุณจะทราบว่าเวอร์ชันใช้งานได้โดยการตรวจสอบปลายทางในออบเจ็กต์การสมัครใช้บริการที่เป็นผลลัพธ์ หากต้นทางคือ fcm.googleapis.com
แสดงว่าใช้งานได้
https://fcm.googleapis.com/fcm/send/ABCD1234
การส่งข้อความพุช
หากต้องการส่งข้อความโดยใช้ VAPID คุณต้องส่งคำขอโปรโตคอล Web Push แบบปกติที่มีส่วนหัว HTTP เพิ่มเติม 2 รายการ ได้แก่ ส่วนหัวการให้สิทธิ์และส่วนหัว Crypto-Key
ส่วนหัวการให้สิทธิ์
ส่วนหัว Authorization
คือ JSON Web Token (JWT) ที่ลงนามโดยมี "WebPush" อยู่ด้านหน้า
JWT คือวิธีการแชร์ออบเจ็กต์ JSON กับบุคคลที่ 2 ในลักษณะที่ฝ่ายที่ส่งสามารถลงนามได้ และฝ่ายที่รับสามารถยืนยันได้ว่าลายเซ็นนั้นมาจากผู้ส่งที่คาดไว้ โครงสร้างของ JWT คือสตริงที่เข้ารหัส 3 สตริงและมีจุด 1 จุดคั่นระหว่างสตริง
<JWTHeader>.<Payload>.<Signature>
ส่วนหัว JWT
ส่วนหัว JWT มีชื่ออัลกอริทึมที่ใช้สำหรับการลงชื่อและประเภทของโทเค็น สำหรับ VAPID ค่านี้ต้องมีลักษณะดังนี้
{
"typ": "JWT",
"alg": "ES256"
}
จากนั้นจะเป็น URL แบบ Base64 ที่เข้ารหัสและกลายเป็นส่วนแรกของ JWT
เพย์โหลด
เพย์โหลดเป็นออบเจ็กต์ JSON อีกรายการที่มีข้อมูลต่อไปนี้
- กลุ่มเป้าหมาย ("aud")
- แหล่งที่มาของบริการพุช (ไม่ใช่ต้นทางของเว็บไซต์)
ใน JavaScript คุณสามารถดำเนินการต่อไปนี้เพื่อให้ได้กลุ่มเป้าหมาย
const audience = new URL(subscription.endpoint).origin
- แหล่งที่มาของบริการพุช (ไม่ใช่ต้นทางของเว็บไซต์)
ใน JavaScript คุณสามารถดำเนินการต่อไปนี้เพื่อให้ได้กลุ่มเป้าหมาย
- เวลาหมดอายุ ("exp")
- ซึ่งเป็นจำนวนวินาทีที่ระบบจะพิจารณาว่าคำขอหมดอายุ ซึ่งต้องอยู่ภายใน 24 ชั่วโมงหลังจากส่งคำขอใน UTC
- เรื่อง ("sub")
- หัวเรื่องต้องเป็น URL หรือ URL
mailto:
ซึ่งจะระบุผู้ติดต่อในกรณีที่บริการพุชจำเป็นต้องติดต่อผู้ส่งข้อความ
- หัวเรื่องต้องเป็น URL หรือ URL
ตัวอย่างเพย์โหลดอาจมีลักษณะดังนี้
{
"aud": "http://push-service.example.com",
"exp": Math.floor((Date.now() / 1000) + (12 * 60 * 60)),
"sub": "mailto: my-email@some-url.com"
}
ออบเจ็กต์ JSON นี้เข้ารหัส URL base64 และสร้างส่วนที่สองของ JWT
ลายเซ็น
ลายเซ็นเกิดจากการรวมส่วนหัวและเพย์โหลดที่เข้ารหัสโดยมีจุด จากนั้นเข้ารหัสผลลัพธ์โดยใช้คีย์ส่วนตัว VAPID ที่คุณสร้างไว้ก่อนหน้านี้ ผลลัพธ์ควรจะมีจุดต่อท้ายส่วนหัว
เราจะไม่แสดงตัวอย่างโค้ดในเรื่องนี้เนื่องจากมีไลบรารีจำนวนหนึ่งที่จะรับค่าส่วนหัวและเพย์โหลดออบเจ็กต์ JSON และสร้างลายเซ็นนี้ให้คุณ
JWT ที่ลงชื่อแล้วจะถูกใช้เป็นส่วนหัว Authorization ที่มี "WebPush" นำหน้า และจะมีลักษณะดังนี้
WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTQ2NjY2ODU5NCwic3ViIjoibWFpbHRvOnNpbXBsZS1wdXNoLWRlbW9AZ2F1bnRmYWNlLmNvLnVrIn0.Ec0VR8dtf5qb8Fb5Wk91br-evfho9sZT6jBRuQwxVMFyK5S8bhOjk8kuxvilLqTBmDXJM5l3uVrVOQirSsjq0A
โปรดสังเกต 2-3 อย่างเกี่ยวกับเรื่องนี้ อย่างแรก ส่วนหัวการให้สิทธิ์มีคำว่า "WebPush" และควรตามด้วยช่องว่างตามด้วย JWT นอกจากนี้ให้สังเกตจุดที่คั่นส่วนหัว JWT, เพย์โหลด และลายเซ็น
ส่วนหัว Crypto-Key
นอกเหนือจากส่วนหัวการให้สิทธิ์แล้ว คุณต้องเพิ่มคีย์สาธารณะ VAPID ไปยังส่วนหัว Crypto-Key
เป็นสตริงที่เข้ารหัส URL ฐาน 64 โดยมี p256ecdsa=
นำหน้า
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo
เมื่อส่งการแจ้งเตือนที่มีข้อมูลที่เข้ารหัส คุณจะใช้ส่วนหัว Crypto-Key
อยู่แล้ว ดังนั้นในการเพิ่มคีย์แอปพลิเคชันเซิร์ฟเวอร์ ก็แค่เพิ่มเครื่องหมายเซมิโคลอนก่อนเพิ่มเนื้อหาข้างต้น ซึ่งจะส่งผลให้เกิดสิ่งต่อไปนี้
dh=BGEw2wsHgLwzerjvnMTkbKrFRxdmwJ5S_k7zi7A1coR_sVjHmGrlvzYpAT1n4NPbioFlQkIrTNL8EH4V3ZZ4vJE;
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaN
ความเป็นจริงของการเปลี่ยนแปลงเหล่านี้
เมื่อใช้ VAPID คุณจะไม่จำเป็นต้องลงชื่อสมัครใช้บัญชีกับ GCM เพื่อใช้ข้อความ Push ใน Chrome และสามารถใช้เส้นทางโค้ดเดียวกันในการสมัครรับข้อมูลให้กับผู้ใช้และส่งข้อความถึงผู้ใช้ทั้งใน Chrome และ Firefox ทั้ง 2 อย่างเป็นไปตามมาตรฐาน
สิ่งที่คุณต้องคำนึงถึงคือใน Chrome 51 และก่อนหน้านี้ Opera สำหรับเบราว์เซอร์ Android และ Samsung คุณยังคงต้องกำหนด gcm_sender_id
ในไฟล์ Manifest ของเว็บแอปและคุณจะต้องเพิ่มส่วนหัวการให้สิทธิ์ไปยังปลายทาง FCM ที่จะส่งคืน
โดย VAPID มีข้อจำกัดจากข้อกำหนดที่เป็นกรรมสิทธิ์เหล่านี้ ถ้าคุณนำ VAPID มาใช้ก็จะทำงานได้กับทุกเบราว์เซอร์ที่รองรับพุชจากเว็บ เนื่องจากมีเบราว์เซอร์ที่รองรับ VAPID มากขึ้น คุณจึงตัดสินใจได้ว่าควรนำ gcm_sender_id
ออกจากไฟล์ Manifest เมื่อใด