Zalety interoperacyjności Web Push Interop

Jan Kowalski
Joe Medley

Gdy interfejs Chrome po raz pierwszy obsługiwał interfejs Web Push API, opierał się na Komunikacji w chmurze Firebase (FCM),nazywanej wcześniej Google Cloud Messaging (GCM), usługą push. Wymaga to użycia zastrzeżonego interfejsu API. Dzięki temu Chrome mógł udostępnić interfejs Web Push API programistom, gdy specyfikacja protokołu Web Push Protocol była nadal tworzona, a później umożliwiała uwierzytelnianie (czyli zadeklarowanie, że nadawca wiadomości jest tym, za kogo się podaje) w okresie, gdy protokół Web Push Protocol go nie zawiera. Dobra wiadomość: żadna z tych sytuacji nie jest już prawdziwa.

FCM / GCM i Chrome obsługują teraz standardowy protokół Web Push Protocol, natomiast uwierzytelnianie nadawcy można uzyskać, implementując VAPID, co oznacza, że Twoja aplikacja internetowa nie potrzebuje już identyfikatora „gcm_sender_id”.

W tym artykule najpierw opiszę, jak przekonwertować istniejący kod serwera na protokół Web Push Protocol w FCM. Następnie pokażę, jak wdrożyć standard VAPID w kodzie klienta i serwera.

FCM obsługuje protokół Web Push Protocol

Zacznijmy od krótkiego kontekstu. Gdy Twoja aplikacja internetowa rejestruje subskrypcję push, otrzymuje adres URL usługi push. Serwer użyje tego punktu końcowego, aby wysłać dane do użytkownika w aplikacji internetowej. W Chrome otrzymasz punkt końcowy FCM, jeśli zasubskrybujesz użytkownika bez VAPID. (VAPID omówimy później). Zanim usługa FCM obsługiwała protokół Web Push Protocol, trzeba było wyodrębnić identyfikator rejestracji FCM z końca adresu URL i umieścić go w nagłówku przed wysłaniem żądania do interfejsu FCM API. Na przykład punkt końcowy FCM o wartości https://android.googleapis.com/gcm/send/ABCD1234 będzie miał identyfikator rejestracji „ABCD1234”.

Teraz gdy FCM obsługuje protokół Web Push Protocol, możesz pozostawić punkt końcowy bez zmian i używać adresu URL jako punktu końcowego protokołu Web Push Protocol. (Podobnie jak w Firefoksie i w pozostałych przeglądarkach w przyszłości).

Zanim przejdziemy do bardziej szczegółowych informacji o VAPID, musimy się upewnić, że nasz kod serwera prawidłowo obsługuje punkt końcowy FCM. Poniżej znajdziesz przykład wysyłania żądania do usługi push w Node. Zwróć uwagę, że w FCM dodajemy klucz interfejsu API do nagłówków żądań. W przypadku innych punktów końcowych usługi push nie będzie to potrzebne. W przypadku Chrome starszych niż 52, Opera Android i przeglądarki Samsung musisz też dodać „gcm_sender_id” do pliku manifest.json aplikacji internetowej. Klucz interfejsu API i identyfikator nadawcy służą do sprawdzania, czy serwer wysyłający żądania może faktycznie wysyłać 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 chodzi o zmianę interfejsu API FCM / GCM, więc nie musisz aktualizować subskrypcji. Wystarczy zmienić kod serwera i zdefiniować nagłówki, jak pokazano powyżej.

Wprowadzenie do identyfikacji serwerów za pomocą VAPID

VAPID to nowa krótka nazwa „Voluntary Application Server Identification”. Ta nowa specyfikacja definiuje zasadniczo uzgadnianie połączenia między serwerem aplikacji a usługą push oraz pozwala usłudze push na sprawdzenie, która witryna wysyła wiadomości. Dzięki VAPID możesz uniknąć specyficznych dla FCM instrukcji wysyłania wiadomości push. Nie potrzebujesz już projektu Firebase, nagłówka gcm_sender_id ani Authorization.

Ten proces jest dość prosty:

  1. Serwer aplikacji tworzy parę kluczy publiczny/prywatny. Klucz publiczny jest przekazywany Twojej aplikacji internetowej.
  2. Gdy użytkownik zdecyduje się otrzymywać powiadomienia push, dodaj klucz publiczny do obiektu opcji wywołania subscription().
  3. Gdy serwer aplikacji wysyła wiadomość push, dołącz podpisany token sieciowy JSON wraz z kluczem publicznym.

Przyjrzyjmy się im szczegółowo.

Tworzenie pary kluczy (publicznego/prywatnego)

Szyfrowanie jest dla mnie straszne, więc oto odpowiednia sekcja specyfikacji poświęcona formatowi kluczy publicznych/prywatnych VAPID:

Serwery aplikacji POWINNY wygenerować i utrzymać parę kluczy podpisywania, której można użyć z użyciem podpisu cyfrowego krzywych eliptycznych (ECDSA) na krzywej P-256.

Informacje o tym, jak to zrobić, znajdziesz w bibliotece węzłów web-push:

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

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

Subskrybowanie za pomocą klucza publicznego

Aby zasubskrybować dla użytkownika Chrome przekazywanie za pomocą klucza publicznego VAPID klucza publicznego, musisz przekazać klucz publiczny jako tablicę Uint8Track z parametrem applicationServerKey metody subscription().

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

Po przeanalizowaniu punktu końcowego w uzyskanym obiekcie subskrypcji dowiesz się, czy się udało. Jeśli źródło to fcm.googleapis.com, wszystko działa.

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

Wysyłanie wiadomości push

Aby wysłać wiadomość za pomocą VAPID, musisz utworzyć normalne żądanie protokołu 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 przedrostkiem „WebPush” („WebPush”).

JWT to sposób na udostępnienie obiektu JSON drugiej stronie w taki sposób, aby strona wysyłająca mogła go podpisać, a odbiorca mógł sprawdzić, czy podpis pochodzi od oczekiwanego nadawcy. Struktura JWT składa się z 3 zaszyfrowanych ciągów znaków połączonych jedną kropką.

<JWTHeader>.<Payload>.<Signature>

Nagłówek JWT

Nagłówek JWT zawiera nazwę algorytmu używaną do podpisywania i typ tokena. W przypadku VAPID musi to być:

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

Zostaje on zakodowany w base64 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). W języku JavaScript możesz wykonać te czynności, aby uzyskać odbiorców: const audience = new URL(subscription.endpoint).origin
  • Okres ważności („exp”)
    • Jest to liczba sekund, przez które żądanie powinno zostać uznane za wygasłe. Czas ten MUSI przypadać w ciągu 24 godzin od złożenia prośby (czas UTC).
  • Temat („sub”)
    • Tematem musi być adres URL lub adres URL mailto:. Jest to punkt kontaktu 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 za pomocą adresu URL w base64 i stanowi drugą część tokena JWT.

Podpis

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

Nie pokażę teraz przykładowego kodu, ponieważ istnieje wiele bibliotek, które pobierają nagłówek i ładunek obiektów JSON, aby wygenerować ten podpis.

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

WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTQ2NjY2ODU5NCwic3ViIjoibWFpbHRvOnNpbXBsZS1wdXNoLWRlbW9AZ2F1bnRmYWNlLmNvLnVrIn0.Ec0VR8dtf5qb8Fb5Wk91br-evfho9sZT6jBRuQwxVMFyK5S8bhOjk8kuxvilLqTBmDXJM5l3uVrVOQirSsjq0A

Zwróć uwagę na kilka informacji na ten temat. Najpierw nagłówek Authorization zawiera słowo „WebPush” („WebPush”), po którym powinna występować spacja, a po niej token JWT. Zwróć też uwagę na kropki oddzielające nagłówek JWT, ładunek i podpis.

Nagłówek klucza CryptoKey

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

p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo

Gdy wysyłasz powiadomienie z zaszyfrowanymi danymi, korzystasz już z nagłówka Crypto-Key. Aby dodać klucz serwera aplikacji, wystarczy, że dodasz średnik przed dodaniem powyższej treści, co spowoduje:

dh=BGEw2wsHgLwzerjvnMTkbKrFRxdmwJ5S_k7zi7A1coR_sVjHmGrlvzYpAT1n4NPbioFlQkIrTNL8EH4V3ZZ4vJE;
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaN

Realność tych zmian

Dzięki VAPID nie trzeba już zakładać konta w GCM, aby korzystać z push w Chrome. W Chrome i Firefoksie możesz użyć tej samej ścieżki kodu do subskrybowania kanału i wysyłania do niego wiadomości. Oba są zgodne ze standardami.

Pamiętaj, że w Chrome 51 i starszych wersjach, Opera na Androida i przeglądarkach Samsung, nadal musisz zdefiniować parametr gcm_sender_id w pliku manifestu aplikacji internetowej i dodać nagłówek autoryzacji do punktu końcowego FCM, który będzie zwracany.

VAPID zapewnia możliwość korzystania z tych zastrzeżonych wymagań. Jeśli zaimplementujesz standard VAPID, będzie on działał we wszystkich przeglądarkach, które obsługują web push. Coraz więcej przeglądarek obsługuje VAPID, więc możesz zdecydować, kiedy usunąć właściwość gcm_sender_id z pliku manifestu.