L'interopérabilité Web push l'emporte

Matt Gaunt
Joe Medley
Joe Medley

Lorsque Chrome prenait en charge l'API Web Push pour la première fois, elle s'appuyait sur le service push Firebase Cloud Messaging (FCM), anciennement appelé Google Cloud Messaging (GCM). Pour cela, il fallait utiliser son API propriétaire. Cela a permis à Chrome de mettre l'API Web Push à la disposition des développeurs à un moment où la spécification du protocole Web push était encore en cours d'écriture et a permis de fournir une authentification (c'est-à-dire que l'expéditeur du message est bien celui qu'il prétend être) au moment où le protocole Web Push n'était pas disponible. Bonne nouvelle: aucune de ces affirmations n'est vraie.

FCM / GCM et Chrome sont désormais compatibles avec le protocole Web push standard, tandis que l'authentification de l'expéditeur peut être effectuée en implémentant VAPID, ce qui signifie que votre application Web n'a plus besoin d'un identifiant "gcm_sender_id".

Dans cet article, je vais d'abord vous expliquer comment convertir le code de votre serveur existant pour utiliser le protocole Web push avec FCM. Ensuite, je vais vous montrer comment implémenter VAPID dans votre code client et serveur.

FCM est compatible avec le protocole Web Push.

Commençons par un peu de contexte. Lorsque votre application Web s'inscrit à un abonnement push, elle reçoit l'URL d'un service push. Votre serveur utilisera ce point de terminaison pour envoyer des données à votre utilisateur via votre application Web. Dans Chrome, vous recevrez un point de terminaison FCM si vous abonnez un utilisateur sans VAPID. (Nous aborderons VAPID plus tard.) Avant que FCM ne soit compatible avec le protocole Web Push, vous deviez extraire l'ID d'enregistrement FCM à la fin de l'URL et le placer dans l'en-tête avant d'effectuer une requête à l'API FCM. Par exemple, un point de terminaison FCM de https://android.googleapis.com/gcm/send/ABCD1234 serait associé à l'ID d'enregistrement "ABCD1234".

Maintenant que FCM est compatible avec le protocole Web Push, vous pouvez laisser le point de terminaison intact et utiliser l'URL comme point de terminaison du protocole Web Push. Cela s'aligne sur Firefox et, espérons-le, sur tous les autres navigateurs.

Avant de nous plonger dans VAPID, nous devons nous assurer que le code de notre serveur gère correctement le point de terminaison FCM. Vous trouverez ci-dessous un exemple d'envoi d'une requête à un service push dans Node. Notez que pour FCM, nous ajoutons la clé API aux en-têtes de requête. Pour les autres points de terminaison du service push, ce n'est pas nécessaire. Pour les versions antérieures à Chrome 52, Opera Android et le navigateur Samsung, vous devez également inclure "gcm_sender_id" dans le fichier manifest.json de votre application Web. La clé API et l'ID de l'expéditeur permettent de vérifier si le serveur à l'origine des requêtes est effectivement autorisé à envoyer des messages à l'utilisateur destinataire.

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

N'oubliez pas qu'il s'agit d'une modification de l'API FCM / GCM. Vous n'avez donc pas besoin de mettre à jour vos abonnements. Il vous suffit de modifier le code de votre serveur pour définir les en-têtes, comme indiqué ci-dessus.

Présentation de VAPID pour l'identification des serveurs

VAPID est le nouveau nom court sympathique de Voluntary Application Server Identification (Identification du serveur d'applications volontaire). Cette nouvelle spécification définit essentiellement un handshake entre votre serveur d'applications et le service push, et permet à ce dernier de confirmer le site qui envoie des messages. Avec VAPID, vous pouvez éviter les étapes spécifiques à FCM pour envoyer un message push. Vous n'avez plus besoin d'un projet Firebase, d'un gcm_sender_id ni d'un en-tête Authorization.

Le processus est assez simple:

  1. Le serveur d'applications crée une paire de clés publique/privée. La clé publique est attribuée à votre application Web.
  2. Lorsque l'utilisateur décide de recevoir des notifications push, ajoutez la clé publique à l'objet d'options de l'appel "subscribe()".
  3. Lorsque votre serveur d'applications envoie un message push, incluez un jeton Web JSON signé avec la clé publique.

Examinons ces étapes en détail.

Créer une paire de clés publique/privée

Je ne suis pas expert en chiffrement. Voici donc la section pertinente de la spécification concernant le format des clés publiques/privées VAPID:

Les serveurs d'applications DOIVENT générer et gérer une paire de clés de signature utilisable avec la signature numérique à courbe elliptique (ECDSA) sur la courbe P-256.

Vous pouvez voir comment procéder dans la bibliothèque de nœuds web-push:

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

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

S'abonner avec la clé publique

Pour abonner un utilisateur Chrome en vue d'une transmission push à l'aide de la clé publique VAPID, vous devez transmettre cette clé en tant qu'Uint8Array à l'aide du paramètre applicationServerKey de la méthode "subscribe()".

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

Pour savoir si cela a fonctionné, examinez le point de terminaison dans l'objet d'abonnement obtenu. Si l'origine est fcm.googleapis.com, cela fonctionne.

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

Envoyer un message push

Pour envoyer un message à l'aide de VAPID, vous devez effectuer une requête Web Push Protocol standard avec deux en-têtes HTTP supplémentaires: un en-tête d'autorisation et un en-tête Crypto-Key.

En-tête d'autorisation

L'en-tête Authorization est un jeton Web JSON (JWT) signé avec "WebPush" devant lui.

Un jeton JWT est un moyen de partager un objet JSON avec un tiers de sorte que la partie émettrice puisse le signer et que la partie destinataire puisse vérifier que la signature provient de l'expéditeur attendu. La structure d'un jeton JWT est constituée de trois chaînes chiffrées, reliées par un seul point entre elles.

<JWTHeader>.<Payload>.<Signature>

En-tête JWT

L'en-tête JWT contient le nom de l'algorithme utilisé pour la signature et le type de jeton. Pour VAPID, il doit s'agir:

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

Elle est ensuite encodée en URL en base64 et constitue la première partie du jeton JWT.

Charge utile

Il s'agit d'un autre objet JSON contenant les éléments suivants:

  • Audience ("aud")
    • Il s'agit de l'origine du service push (et NON de l'origine de votre site). En JavaScript, vous pouvez obtenir l'audience comme suit: const audience = new URL(subscription.endpoint).origin
  • Délai d'expiration ("exp")
    • Il s'agit du nombre de secondes avant que la requête ne soit considérée comme ayant expiré. Cette valeur DOIT se situer dans les 24 heures suivant l'envoi de la requête, au format UTC.
  • Objet ("sous")
    • L'objet doit être une URL ou une URL mailto:. Cela fournit un point de contact au cas où le service push doit contacter l'expéditeur du message.

Voici un exemple de charge utile:

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

Cet objet JSON est encodé en URL en base64 et constitue la deuxième partie du jeton JWT.

Signature

La signature est le résultat de la jointure de l'en-tête et de la charge utile encodés par un point, puis du chiffrement du résultat à l'aide de la clé privée VAPID que vous avez créée précédemment. Le résultat lui-même doit être ajouté à l'en-tête avec un point.

Je ne vais pas vous montrer d'exemple de code pour cela, car il existe un certain nombre de bibliothèques qui récupèrent les objets JSON d'en-tête et de charge utile pour générer cette signature pour vous.

Le jeton JWT signé est utilisé comme en-tête d'autorisation, suivi du suffixe "WebPush". Il se présente comme suit:

WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTQ2NjY2ODU5NCwic3ViIjoibWFpbHRvOnNpbXBsZS1wdXNoLWRlbW9AZ2F1bnRmYWNlLmNvLnVrIn0.Ec0VR8dtf5qb8Fb5Wk91br-evfho9sZT6jBRuQwxVMFyK5S8bhOjk8kuxvilLqTBmDXJM5l3uVrVOQirSsjq0A

Voici quelques informations importantes à ce sujet. Tout d'abord, l'en-tête "Authorization" contient littéralement le mot "WebPush" et doit être suivi d'un espace, puis du jeton JWT. Notez également les points qui séparent l'en-tête, la charge utile et la signature du jeton JWT.

En-tête Crypto-Key

En plus de l'en-tête d'autorisation, vous devez ajouter votre clé publique VAPID à l'en-tête Crypto-Key en tant que chaîne encodée au format URL en base64 avec le préfixe p256ecdsa=.

p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo

Lorsque vous envoyez une notification contenant des données chiffrées, vous utilisez déjà l'en-tête Crypto-Key. Par conséquent, pour ajouter la clé du serveur d'application, il vous suffit d'ajouter un point-virgule avant d'ajouter le contenu ci-dessus. Voici ce qui se produit:

dh=BGEw2wsHgLwzerjvnMTkbKrFRxdmwJ5S_k7zi7A1coR_sVjHmGrlvzYpAT1n4NPbioFlQkIrTNL8EH4V3ZZ4vJE;
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaN

Réalité de ces changements

Avec VAPID, vous n'avez plus besoin de créer un compte GCM pour utiliser le mode push dans Chrome. Vous pouvez utiliser le même chemin de code pour abonner un utilisateur et lui envoyer un message à la fois dans Chrome et Firefox. Tous deux respectent ces normes.

N'oubliez pas que dans Chrome 51 et les versions antérieures, Opera pour Android et Samsung, vous devrez toujours définir le gcm_sender_id dans le fichier manifeste de votre application Web et ajouter l'en-tête d'autorisation au point de terminaison FCM qui sera renvoyé.

VAPID offre un écart par rapport à ces exigences propriétaires. Si vous implémentez VAPID, elle fonctionnera dans tous les navigateurs compatibles avec la technologie Web push. Étant donné que d'autres navigateurs sont compatibles avec VAPID, vous pouvez décider à quel moment supprimer gcm_sender_id du fichier manifeste.

.