Zalety interoperacyjności Web Push Interop

Joe Medley
Joe Medley

Gdy Chrome po raz pierwszy obsługiwał interfejs Web Push API, korzystał z usługi push Komunikacja w chmurze Firebase (FCM), która wcześniej była znana jako Google Cloud Messaging (GCM). Wymaga to użycia zastrzeżonego interfejsu API. Dzięki temu Chrome mogła udostępnić interfejs Web Push API deweloperom w czasie, gdy specyfikacja protokołu Web Push była jeszcze tworzona, a później zapewnić uwierzytelnianie (czyli potwierdzenie tożsamości nadawcy wiadomości) w czasie, gdy protokół Web Push nie miał jeszcze tej funkcji. Dobra wiadomość: żadna z tych rzeczy nie jest już prawdziwa.

FCM / GCM i Chrome obsługują teraz standardowy protokół Web Push, a uwierzytelnianie nadawcy można uzyskać dzięki wdrożeniu VAPID, co oznacza, że Twoja aplikacja internetowa nie musi już zawierać identyfikatora „gcm_sender_id”.

W tym artykule najpierw opiszę, jak przekonwertować dotychczasowy kod serwera, aby używać protokołu Web Push w połączeniu z FCM. Następnie pokażę Ci, jak zaimplementować VAPID w kodzie klienta i serwera.

FCM obsługuje protokół Web Push

Zacznijmy od przedstawienia kontekstu. Gdy aplikacja internetowa rejestruje się w subskrypcji powiadomień push, otrzymuje adres URL usługi powiadomień push. Tego punktu końcowego używa Twój serwer do wysyłania danych do użytkownika za pomocą aplikacji internetowej. W Chrome otrzymasz punkt końcowy FCM, jeśli subskrybujesz użytkownika bez VAPID. (omówimy je później). Zanim FCM obsługiwał protokół Web Push, przed wysłaniem żądania do interfejsu FCM API należało wyodrębnić identyfikator rejestracji FCM z końca adresu URL i umieścić go w nagłówku. Na przykład punkt końcowy FCM o adresie https://android.googleapis.com/gcm/send/ABCD1234 miałby identyfikator rejestracji „ABCD1234”.

Ponieważ FCM obsługuje protokół Web Push, możesz pozostawić punkt końcowy w swoim pierwotnym stanie i używać adresu URL jako punktu końcowego protokołu Web Push. (W ten sposób dopasowujemy je do przeglądarki Firefox i mamy nadzieję, że także do innych przeglądarek w przyszłości).

Zanim przejdziemy do VAPID, musimy się upewnić, że kod serwera prawidłowo obsługuje punkt końcowy FCM. Poniżej znajdziesz przykład wysyłania żądania do usługi push w Node. Pamiętaj, że w przypadku FCM dodajemy klucz interfejsu API do nagłówków żądania. W przypadku innych punktów końcowych usługi push nie jest to wymagane. W przypadku Chrome w wersji wcześniejszej niż 52, przeglądarki Opera na Androida i przeglądarki Samsunga nadal musisz też uwzględnić parametr „gcm_sender_id” w pliku manifest.json aplikacji internetowej. Klucz interfejsu API i identyfikator nadawcy służą do sprawdzania, czy serwer przesyłający żądania ma rzeczywiście uprawnienia do wysyłania wiadomości do użytkownika odbierającego.

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

Pamiętaj, że jest to zmiana interfejsu FCM / GCM, więc nie musisz aktualizować subskrypcji. Zamiast tego zmień kod serwera, aby zdefiniować nagłówki zgodnie z opisem powyżej.

Wprowadzenie VAPID na potrzeby identyfikacji serwera

VAPID to nowa, krótka nazwa identyfikacji serwera aplikacji na żądanie. Ta nowa specyfikacja definiuje w podstawie procedurę uzgadniania połączenia między serwerem aplikacji a usługą przesyłania powiadomień push i pozwala usłudze przesyłania powiadomień push potwierdzić, która witryna wysyła powiadomienia. Dzięki VAPID możesz pominąć kroki związane z wysyłaniem wiadomości push w ramach FCM. Nie musisz już używać projektu Firebase, nagłówka gcm_sender_id ani nagłówka Authorization.

Proces jest dość prosty:

  1. Serwer aplikacji tworzy parę kluczy publiczno-prywatnych. Klucz publiczny jest przekazywany do aplikacji internetowej.
  2. Gdy użytkownik zdecyduje się na otrzymywanie powiadomień push, dodaj klucz publiczny do obiektu opcji wywołania subscribe().
  3. Gdy serwer aplikacji wysyła komunikat push, dołącz podpisany token sieciowy JSON wraz z kluczem publicznym.

Przyjrzyjmy się tym krokom bardziej szczegółowo.

Tworzenie pary kluczy (publicznego i prywatnego)

Nie znam się na szyfrowaniu, więc poniżej podaję odpowiednią sekcję specyfikacji dotyczącej formatu kluczy publicznych i prywatnych VAPID:

Serwery aplikacji POWINNY generować i utrzymywać parę kluczy podpisywania, która może być używana z podpisem cyfrowym wykorzystującym krzywe eliptyczne (ECDSA) na krzywej P-256.

Więcej informacji znajdziesz w bibliotece węzła web-push:

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

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

Subskrypcja za pomocą klucza publicznego

Aby zasubskrybować użytkownika Chrome do otrzymywania powiadomień push za pomocą klucza publicznego VAPID, musisz przekazać klucz publiczny jako tablicę Uint8 za pomocą parametru applicationServerKey metody subscribe().

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

Aby sprawdzić, czy udało się to zrobić, sprawdź punkt końcowy w wynikającym obiekcie subskrypcji. Jeśli źródło to fcm.googleapis.com, oznacza to, że wszystko działa prawidłowo.

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

Wysyłanie powiadomienia push

Aby wysłać wiadomość za pomocą VAPID, musisz przesłać zwykłe żądanie Web Push Protocol z 2 dodatkowymi nagłówkami HTTP: nagłówkiem Authorization i nagłówkiem Crypto-Key.

Nagłówek autoryzacji

Nagłówek Authorization to podpisany token sieciowy JSON (JWT) z dodatkiem prefiksu „WebPush”.

Token JWT to sposób udostępniania obiektu JSON drugiej stronie w taki sposób, aby strona wysyłająca mogła go podpisać, a strona odbierająca mogła zweryfikować podpis, aby upewnić się, że pochodzi on od oczekiwanego nadawcy. Struktura tokena JWT to 3 zaszyfrowane ciągi znaków połączone jedną kropką.

<JWTHeader>.<Payload>.<Signature>

Nagłówek JWT

Nagłówek JWT zawiera nazwę algorytmu używanego do podpisywania oraz typ tokena. W przypadku VAPID musisz:

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

Następnie jest on kodowany w standardzie base64url i stanowi pierwszą część tokena JWT.

Ładunek

Ładunek to kolejny obiekt JSON zawierający:

  • Odbiorcy („aud”):
    • To jest źródło usługi push (NIE źródło Twojej witryny). Aby uzyskać listę odbiorców, możesz wykonać w JavaScript te czynności: const audience = new URL(subscription.endpoint).origin
  • Czas wygaśnięcia („exp”)
    • Jest to liczba sekund, po której żądanie powinno zostać uznane za wygasłe. MUSI to nastąpić w ciągu 24 godzin od wysłania prośby (według czasu UTC).
  • Temat („sub”)
    • Temat musi być adresem URL lub adresem URL mailto:. Jest to punkt kontaktowy na wypadek, gdyby usługa push musiała skontaktować się z nadawcą wiadomości.

Przykładowy ładunek może wyglądać tak:

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

Ten obiekt JSON jest zakodowany w formacie URL base64 i stanowi drugą część tokena JWT.

Podpis

Podpis jest wynikiem złączenia zakodowanego nagłówka i ładunku za pomocą kropki, a następnie zaszyfrowania tego wyniku za pomocą utworzonego wcześniej klucza prywatnego VAPID. Wynik powinien być dołączony do nagłówka za pomocą kropki.

Nie pokażę przykładu kodu, ponieważ istnieje wiele bibliotek, które pobierają obiekty nagłówka i ładunku JSON i generują na ich podstawie podpis.

Podpisany token JWT jest używany jako nagłówek Authorization z dołączonym do niego ciągiem „WebPush” i wygląda mniej więcej tak:

WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTQ2NjY2ODU5NCwic3ViIjoibWFpbHRvOnNpbXBsZS1wdXNoLWRlbW9AZ2F1bnRmYWNlLmNvLnVrIn0.Ec0VR8dtf5qb8Fb5Wk91br-evfho9sZT6jBRuQwxVMFyK5S8bhOjk8kuxvilLqTBmDXJM5l3uVrVOQirSsjq0A

Zwróć uwagę na kilka kwestii. Po pierwsze, nagłówek Authorization zawiera dosłownie słowo „WebPush”, po którym powinna następować spacja, a następnie token JWT. Zwróć też uwagę na kropki oddzielające nagłówek, ładunek i podpis JWT.

Nagłówek Crypto-Key

Oprócz nagłówka Authorization musisz dodać klucz publiczny VAPID do nagłówka Crypto-Key jako ciąg znaków zakodowany w formacie base64 z dołączonym prefiksem p256ecdsa=.

p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo

Gdy wysyłasz powiadomienie z zaszyfrowanymi danymi, używasz już nagłówka Crypto-Key, więc aby dodać klucz serwera aplikacji, musisz przed dodaniem treści powyżej dodać przecinek:

dh=BGEw2wsHgLwzerjvnMTkbKrFRxdmwJ5S_k7zi7A1coR_sVjHmGrlvzYpAT1n4NPbioFlQkIrTNL8EH4V3ZZ4vJE;
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaN

Realizacja tych zmian

Dzięki VAPID nie musisz już rejestrować konta w GCM, aby korzystać z powiadomień push w Chrome. Możesz też używać tego samego kodu do rejestrowania użytkownika i wysyłania mu wiadomości zarówno w Chrome, jak i w Firefoksie. Oba są zgodne ze standardami.

Pamiętaj, że w Chrome 51 i starszych wersjach, a także w Opera na Androida i w przeglądarce Samsung musisz zdefiniować gcm_sender_idw manifeście aplikacji internetowej i dodać nagłówek Authorization do zwracanego punktu końcowego FCM.

VAPID umożliwia wyłączenie tych wymagań. Jeśli wdrożysz VAPID, będzie on działać we wszystkich przeglądarkach, które obsługują web push. Gdy więcej przeglądarek będzie obsługiwać VAPID, możesz zdecydować, kiedy usunąć gcm_sender_id z pliku manifestu.