Chiến thắng khả năng tương tác đẩy của web

Matt Gaunt
Liên khúc Joe
Joe Medley

Trong lần đầu tiên hỗ trợ Web Push API, Chrome dựa vào dịch vụ đẩy của Giải pháp gửi thông báo qua đám mây của Firebase (FCM), trước đây là Giải pháp gửi thông báo qua đám mây của Google (GCM). Để làm được điều này, bạn phải sử dụng API độc quyền của Google Play. Điều này cho phép Chrome cung cấp Web Push API cho nhà phát triển tại thời điểm mà thông số kỹ thuật Web Push Protocol vẫn đang được ghi và sau đó cung cấp thông tin xác thực (nghĩa là người gửi thông báo là người mà họ nói) tại thời điểm mà Giao thức đẩy web không có thông số đó. Tin vui là cả hai điều này đều không đúng.

FCM / GCM và Chrome hiện hỗ trợ Giao thức đẩy web tiêu chuẩn, trong khi có thể xác thực người gửi bằng cách triển khai VAPID, nghĩa là ứng dụng web của bạn không còn cần "gcm_sender_id" nữa.

Trong bài viết này, trước tiên, tôi sẽ mô tả cách chuyển đổi mã máy chủ hiện có để sử dụng Giao thức đẩy web với FCM. Tiếp theo, tôi sẽ hướng dẫn bạn cách triển khai VAPID trong cả mã ứng dụng và mã máy chủ.

FCM hỗ trợ Giao thức đẩy web

Hãy bắt đầu bằng một chút ngữ cảnh. Khi đăng ký một gói thuê bao đẩy, ứng dụng web của bạn sẽ được cấp URL của dịch vụ đẩy. Máy chủ sẽ sử dụng điểm cuối này để gửi dữ liệu cho người dùng thông qua ứng dụng web. Trong Chrome, bạn sẽ được cấp một điểm cuối FCM nếu đăng ký người dùng không sử dụng VAPID. (Chúng tôi sẽ đề cập đến VAPID sau). Trước khi FCM hỗ trợ Giao thức đẩy web, bạn phải trích xuất mã đăng ký FCM từ cuối URL và đưa mã này vào tiêu đề trước khi gửi yêu cầu API FCM. Ví dụ: điểm cuối FCM của https://android.googleapis.com/gcm/send/ABCD1234 sẽ có mã đăng ký là "ABCD1234".

Giờ đây, FCM hỗ trợ Giao thức đẩy web, bạn có thể giữ nguyên điểm cuối và sử dụng URL làm điểm cuối Giao thức đẩy web. (Điều này phù hợp với Firefox và hy vọng rằng mọi trình duyệt khác trong tương lai.)

Trước khi tìm hiểu sâu hơn về VAPID, chúng tôi cần đảm bảo mã máy chủ của chúng tôi xử lý chính xác điểm cuối FCM. Dưới đây là ví dụ về cách đưa ra yêu cầu tới dịch vụ đẩy trong Nút. Lưu ý rằng đối với FCM, chúng tôi sẽ thêm khoá API vào tiêu đề yêu cầu. Các điểm cuối của dịch vụ đẩy khác là không cần thiết. Đối với Chrome trước phiên bản 52, Opera Android và Trình duyệt Samsung, bạn vẫn được yêu cầu thêm một 'gcm_sender_id' vào tệp manifest.json của ứng dụng web. Khoá API và mã nhận dạng người gửi được dùng để kiểm tra xem máy chủ đưa ra yêu cầu có thực sự được phép gửi thông báo đến người nhận hay không.

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');
    }
});

Hãy nhớ rằng đây là thay đổi đối với API của FCM / GCM, vì vậy, bạn không cần cập nhật các gói thuê bao của mình, mà chỉ cần thay đổi mã máy chủ để xác định các tiêu đề như hiển thị ở trên.

Ra mắt tính năng VAPID để nhận dạng máy chủ

VAPID là tên viết tắt mới và thú vị của "Tình nguyện nhận dạng máy chủ ứng dụng". Về cơ bản, thông số kỹ thuật mới này xác định sự bắt tay giữa máy chủ ứng dụng và dịch vụ đẩy, đồng thời cho phép dịch vụ đẩy xác nhận trang web nào đang gửi thông báo. Với VAPID, bạn có thể tránh được các bước dành riêng cho FCM khi gửi thông báo đẩy. Bạn không cần dự án Firebase, gcm_sender_id hay tiêu đề Authorization nữa.

Quy trình khá đơn giản:

  1. Máy chủ ứng dụng của bạn tạo một cặp khoá công khai/riêng tư. Khoá công khai được cung cấp cho ứng dụng web của bạn.
  2. Khi người dùng chọn nhận thông báo đẩy, hãy thêm khoá công khai vào đối tượng tuỳ chọn của lệnh gọi subscription().
  3. Khi máy chủ ứng dụng của bạn gửi một thông báo đẩy, hãy thêm một Mã thông báo web JSON đã ký cùng với khoá công khai.

Hãy cùng tìm hiểu chi tiết các bước này.

Tạo một cặp khoá công khai/riêng tư

Tôi rất kém trong việc mã hoá, vì vậy, sau đây là phần có liên quan trong thông số kỹ thuật về định dạng của khoá công khai/riêng tư VAPID:

Application Server NÊN tạo and Duy trì một cặp khoá ký có thể sử dụng với đường cong hình elip có chữ ký số (ECDSA) trên đường cong P-256.

Bạn có thể xem cách thực hiện việc này trong thư viện nút đẩy web:

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

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

Đăng ký khoá công khai

Để đăng ký đẩy thông qua khoá công khai VAPID cho người dùng Chrome, bạn cần chuyển khoá công khai dưới dạng Uint8Array bằng cách sử dụng thông số applicationServerKey của phương thức subscription().

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

Bạn sẽ biết tính năng này đã hoạt động hay chưa bằng cách kiểm tra điểm cuối trong đối tượng thuê bao thu được. Nếu nguồn gốc là fcm.googleapis.com, thì có nghĩa là điểm cuối đó đang hoạt động.

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

Gửi thông báo đẩy

Để gửi thông báo bằng VAPID, bạn cần yêu cầu Giao thức đẩy web thông thường với hai tiêu đề HTTP bổ sung: tiêu đề Uỷ quyền và tiêu đề Khoá mã hoá.

Tiêu đề uỷ quyền

Tiêu đề Authorization là một Mã thông báo web JSON (JWT) đã ký có "WebPush" ở phía trước.

JWT là một cách để chia sẻ đối tượng JSON với bên thứ hai theo cách bên gửi có thể ký và bên nhận có thể xác minh chữ ký là từ người gửi dự kiến. Cấu trúc của JWT là 3 chuỗi đã mã hoá và kết hợp với một dấu chấm ở giữa.

<JWTHeader>.<Payload>.<Signature>

Tiêu đề JWT

Tiêu đề JWT chứa tên thuật toán dùng để ký và loại mã thông báo. Đối với VAPID, nội dung này phải là:

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

URL này sau đó được mã hoá url base64 và tạo thành phần đầu tiên của JWT.

Dung lượng

Payload là một đối tượng JSON khác có chứa thông tin sau:

  • Đối tượng ("aud")
    • Đây là nguồn gốc của dịch vụ đẩy (KHÔNG PHẢI là nguồn gốc của trang web của bạn). Trong JavaScript, bạn có thể làm như sau để có được đối tượng: const audience = new URL(subscription.endpoint).origin
  • Thời gian hết hạn ("exp")
    • Đây là số giây cho đến khi yêu cầu được coi là đã hết hạn. Việc này PHẢI trong vòng 24 giờ kể từ khi bạn đưa ra yêu cầu, theo giờ UTC.
  • Tiêu đề ("sub")
    • Tiêu đề phải là một URL hoặc URL mailto:. Thao tác này cung cấp đầu mối liên hệ trong trường hợp dịch vụ đẩy cần liên hệ với người gửi tin nhắn.

Tải trọng mẫu có thể có dạng như sau:

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

Đối tượng JSON này được mã hoá url base64 và tạo thành phần thứ hai của JWT.

Chữ ký

Chữ ký là kết quả của việc kết hợp tiêu đề và tải trọng được mã hoá bằng một dấu chấm, sau đó mã hoá kết quả bằng khoá riêng tư VAPID mà bạn tạo trước đó. Kết quả phải được thêm vào tiêu đề có dấu chấm.

Tôi sẽ không cho xem mã mẫu cho trường hợp này vì có một số thư viện sẽ lấy tiêu đề và các đối tượng JSON tải trọng và tạo chữ ký này cho bạn.

JWT đã ký được dùng làm tiêu đề Uỷ quyền và thêm vào trước "WebPush" sẽ có dạng như sau:

WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTQ2NjY2ODU5NCwic3ViIjoibWFpbHRvOnNpbXBsZS1wdXNoLWRlbW9AZ2F1bnRmYWNlLmNvLnVrIn0.Ec0VR8dtf5qb8Fb5Wk91br-evfho9sZT6jBRuQwxVMFyK5S8bhOjk8kuxvilLqTBmDXJM5l3uVrVOQirSsjq0A

Hãy lưu ý một vài điều về việc này. Trước tiên, tiêu đề Uỷ quyền theo nghĩa đen có từ "WebPush" phải theo sau là một dấu cách, rồi đến JWT. Ngoài ra, hãy lưu ý các dấu chấm phân tách tiêu đề JWT, tải trọng và chữ ký.

Tiêu đề Khoá mã hoá

Cũng như tiêu đề Uỷ quyền, bạn phải thêm khoá công khai VAPID vào tiêu đề Crypto-Key dưới dạng chuỗi được mã hoá url base64 có p256ecdsa= được thêm vào trước.

p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo

Khi gửi thông báo có dữ liệu đã mã hoá, bạn đã sử dụng tiêu đề Crypto-Key. Vì vậy, để thêm khoá máy chủ ứng dụng, bạn chỉ cần thêm dấu chấm phẩy trước khi thêm nội dung ở trên, dẫn đến:

dh=BGEw2wsHgLwzerjvnMTkbKrFRxdmwJ5S_k7zi7A1coR_sVjHmGrlvzYpAT1n4NPbioFlQkIrTNL8EH4V3ZZ4vJE;
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaN

Thực tế của những thay đổi này

Với VAPID, bạn không cần đăng ký tài khoản với GCM để sử dụng tính năng đẩy trong Chrome nữa và bạn có thể sử dụng cùng một đường dẫn mã để đăng ký người dùng và gửi tin nhắn cho người dùng trong cả Chrome và Firefox. Cả hai đều tuân thủ các tiêu chuẩn.

Điều bạn cần lưu ý là trong Chrome 51 trở về trước, Opera dành cho trình duyệt Android và Samsung, bạn vẫn cần xác định gcm_sender_id trong tệp kê khai ứng dụng web và cần thêm tiêu đề Uỷ quyền vào điểm cuối FCM sẽ được trả về.

VAPID giúp bạn đáp ứng các yêu cầu về quyền sở hữu riêng này. Nếu bạn triển khai VAPID, thì tính năng này sẽ hoạt động trên mọi trình duyệt hỗ trợ tính năng đẩy dữ liệu web. Vì ngày càng có nhiều trình duyệt hỗ trợ VAPID, nên bạn có thể quyết định thời điểm bỏ gcm_sender_id khỏi tệp kê khai.