L'interopérabilité Web push l'emporte

Joe Medley
Joe Medley

Lorsque Chrome a commencé à prendre en charge l'API Web Push, il s'appuyait sur le service de push Firebase Cloud Messaging (FCM), anciennement appelé Google Cloud Messaging (GCM). Pour ce faire, vous deviez 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 qui fournissait ensuite une authentification (c'est-à-dire que l'expéditeur du message était bien celui qu'il prétend être) alors que le protocole Web push n'en avait pas besoin. Bonne nouvelle: ce n'est plus le cas.

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. Par conséquent, votre application Web n'a plus besoin d'un "gcm_sender_id".

Dans cet article, je vais d'abord vous expliquer comment convertir votre code serveur existant pour qu'il utilise le protocole Web Push avec FCM. Je vais ensuite vous montrer comment implémenter VAPID dans le 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 pour 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 prenne en charge 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'envoyer une requête API FCM. Par exemple, un point de terminaison FCM de https://android.googleapis.com/gcm/send/ABCD1234 aurait un ID d'enregistrement de '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 le met en conformité avec Firefox et, espérons-le, tous les autres futurs navigateurs.)

Avant de nous intéresser à VAPID, nous devons nous assurer que notre code serveur gère correctement le point de terminaison FCM. Vous trouverez ci-dessous un exemple de requête envoyée à un service de transfert dans Node. Notez que pour FCM, nous ajoutons la clé API aux en-têtes de requête. Pour les autres points de terminaison de service de transfert, ce n'est pas nécessaire. Pour Chrome version antérieure à 52, Opera Android et le navigateur Samsung, vous devez également inclure un "gcm_sender_id" dans le fichier manifeste.json de votre application Web. La clé API et l'ID d'expéditeur sont utilisés pour vérifier si le serveur qui envoie les requêtes est réellement 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 de 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 de l'identification volontaire du serveur d'applications. Cette nouvelle spécification définit essentiellement un handshake entre votre serveur d'application et le service push, et permet au service push de confirmer quel site envoie des messages. Avec VAPID, vous pouvez éviter les étapes spécifiques à FCM pour l'envoi d'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. Votre serveur d'application crée une paire de clés publique/privée. La clé publique est fournie à votre application Web.
  2. Lorsque l'utilisateur choisit de recevoir des notifications push, ajoutez la clé publique à l'objet d'options de l'appel subscribe().
  3. Lorsque votre serveur d'application 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 suis nul 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 une signature numérique à courbe elliptique (ECDSA) sur la courbe P-256.

Pour savoir comment procéder, consultez 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 aux notifications push à l'aide de la clé publique VAPID, vous devez transmettre la clé publique en tant que 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 généré. 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 normale avec deux en-têtes HTTP supplémentaires: un en-tête d'autorisation et un en-tête de clé cryptographique.

En-tête d'autorisation

L'en-tête Authorization est un jeton Web JSON (JWT) signé portant le préfixe "WebPush".

Un JWT permet de partager un objet JSON avec une deuxième partie de manière à ce 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 se compose de trois chaînes chiffrées, reliées par un point unique.

<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"
}

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

Charge utile

La charge utile est un autre objet JSON contenant les éléments suivants:

  • Audience ("aud")
    • Il s'agit de l'origine du service push (PAS l'origine de votre site). En JavaScript, vous pouvez procéder comme suit pour obtenir l'audience: const audience = new URL(subscription.endpoint).origin
  • Date/Heure d'expiration ("exp")
    • Il s'agit du nombre de secondes avant que la requête ne soit considérée comme expirée. Cette opération DOIT être effectuée dans les 24 heures suivant la demande, en UTC.
  • Objet ("sub")
    • L'objet doit être une URL ou une URL mailto:. Cela fournit un point de contact au cas où le service push aurait besoin de 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 base64 et constitue la deuxième partie du jeton JWT.

Signature

La signature est le résultat de la jonction de l'en-tête et de la charge utile encodées 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 un certain nombre de bibliothèques vont prendre les objets JSON d'en-tête et de charge utile, et générer cette signature pour vous.

Le jeton JWT signé est utilisé comme en-tête d'autorisation avec "WebPush" ajouté au début. Il se présente comme suit:

WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTQ2NjY2ODU5NCwic3ViIjoibWFpbHRvOnNpbXBsZS1wdXNoLWRlbW9AZ2F1bnRmYWNlLmNvLnVrIn0.Ec0VR8dtf5qb8Fb5Wk91br-evfho9sZT6jBRuQwxVMFyK5S8bhOjk8kuxvilLqTBmDXJM5l3uVrVOQirSsjq0A

Notez quelques points à ce sujet. Tout d'abord, l'en-tête Authorization contient littéralement le mot "WebPush" et doit être suivi d'une 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 de clé cryptographique

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

p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo

Lorsque vous envoyez une notification avec des données chiffrées, vous utilisez déjà l'en-tête Crypto-Key. Pour ajouter la clé du serveur d'application, il vous suffit d'ajouter un point-virgul avant d'ajouter le contenu ci-dessus, ce qui donne:

dh=BGEw2wsHgLwzerjvnMTkbKrFRxdmwJ5S_k7zi7A1coR_sVjHmGrlvzYpAT1n4NPbioFlQkIrTNL8EH4V3ZZ4vJE;
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaN

La réalité de ces changements

Avec VAPID, vous n'avez plus besoin de créer un compte auprès de GCM pour utiliser le push dans Chrome. Vous pouvez utiliser le même chemin de code pour abonner un utilisateur et lui envoyer un message dans Chrome et Firefox. Les deux respectent les normes.

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

VAPID permet d'éviter ces exigences propriétaires. La mise en œuvre de la fonction VAPID fonctionne dans tous les navigateurs compatibles avec la fonctionnalité Web push. Étant donné que de plus en plus de navigateurs sont compatibles avec VAPID, vous pouvez décider quand supprimer gcm_sender_id de votre fichier manifeste.

.