S'abonner à un utilisateur

La première étape consiste à obtenir l'autorisation de l'utilisateur pour lui envoyer des messages push, puis nous pouvons obtenir un PushSubscription.

L'API JavaScript pour ce faire est relativement simple. Voyons donc le flux logique.

Détection de fonctionnalités

Nous devons d'abord vérifier si le navigateur actuel est compatible avec la messagerie push. Nous pouvons vérifier si la méthode push est prise en charge à l'aide de deux vérifications simples.

  1. Recherchez serviceWorker sur navigator.
  2. Recherchez PushManager dans window.
if (!('serviceWorker' in navigator)) {
  // Service Worker isn't supported on this browser, disable or hide UI.
  return;
}

if (!('PushManager' in window)) {
  // Push isn't supported on this browser, disable or hide UI.
  return;
}

Bien que la compatibilité des navigateurs avec les service workers et les messages push augmente rapidement, il est toujours recommandé de détecter les fonctionnalités pour les deux et de les améliorer progressivement.

Enregistrer un service worker

Grâce à la détection de fonctionnalités, nous savons que les service workers et le push sont compatibles. L'étape suivante consiste à "enregistrer" notre service worker.

Lorsque nous enregistrons un service worker, nous indiquons au navigateur où se trouve notre fichier de service worker. Le fichier n'est toujours que du code JavaScript, mais le navigateur lui "donne accès" aux API du service worker, y compris à la méthode push. Pour être plus précis, le navigateur exécute le fichier dans un environnement de service worker.

Pour enregistrer un service worker, appelez navigator.serviceWorker.register() en transmettant le chemin d'accès à notre fichier. Par exemple :

function registerServiceWorker() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      console.log('Service worker successfully registered.');
      return registration;
    })
    .catch(function (err) {
      console.error('Unable to register service worker.', err);
    });
}

Cette fonction indique au navigateur que nous disposons d'un fichier de service worker et de son emplacement. Dans ce cas, le fichier du service worker se trouve dans /service-worker.js. En arrière-plan, le navigateur procède comme suit après avoir appelé register():

  1. Téléchargez le fichier du service worker.

  2. Exécutez le code JavaScript.

  3. Si tout fonctionne correctement et qu'aucune erreur ne se produit, la promesse renvoyée par register() se résout. En cas d'erreurs, la promesse est rejetée.

Si register() est rejeté, vérifiez dans Chrome DevTools qu'il n'y a pas de fautes de frappe ni d'erreurs dans votre code JavaScript.

Lorsque register() est résolu, il renvoie un ServiceWorkerRegistration. Nous utiliserons cet enregistrement pour accéder à l'API PushManager.

Compatibilité de l'API PushManager avec les navigateurs

Navigateurs pris en charge

  • Chrome: 42.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 16.

Source

Demander une autorisation

Nous avons enregistré notre service worker et sommes prêts à abonner l'utilisateur. L'étape suivante consiste à obtenir l'autorisation de l'utilisateur pour lui envoyer des messages push.

L'API permettant d'obtenir une autorisation est relativement simple. L'inconvénient est que l'API a récemment remplacé la prise en charge d'un rappel par la génération d'une promesse. Le problème est que nous ne pouvons pas savoir quelle version de l'API est implémentée par le navigateur actuel. Vous devez donc implémenter les deux et les gérer.

function askPermission() {
  return new Promise(function (resolve, reject) {
    const permissionResult = Notification.requestPermission(function (result) {
      resolve(result);
    });

    if (permissionResult) {
      permissionResult.then(resolve, reject);
    }
  }).then(function (permissionResult) {
    if (permissionResult !== 'granted') {
      throw new Error("We weren't granted permission.");
    }
  });
}

Dans le code ci-dessus, l'extrait de code important est l'appel à Notification.requestPermission(). Cette méthode affiche une invite à l'utilisateur:

Requête d'autorisation dans Chrome sur ordinateur et mobile

Une fois que l'utilisateur a interagi avec l'invite d'autorisation en appuyant sur "Autoriser", "Bloquer" ou simplement en la fermant, le résultat nous est fourni sous forme de chaîne: 'granted', 'default' ou 'denied'.

Dans l'exemple de code ci-dessus, la promesse renvoyée par askPermission() se résout si l'autorisation est accordée. Sinon, une erreur est générée, ce qui entraîne le rejet de la promesse.

Vous devez gérer un cas particulier : si l'utilisateur clique sur le bouton "Bloquer". Dans ce cas, votre application Web ne pourra plus demander à l'utilisateur l'autorisation. Ils devront "débloquer " manuellement votre application en modifiant son état d'autorisation, qui se trouve dans un panneau de paramètres. Réfléchissez bien à la manière et au moment de demander l'autorisation à l'utilisateur, car s'il clique sur "Bloquer", il n'est pas facile d'annuler cette décision.

La bonne nouvelle est que la plupart des utilisateurs sont prêts à accorder l'autorisation tant qu'ils savent pourquoi elle est demandée.

Nous verrons plus tard comment certains sites populaires demandent des autorisations.

Abonner un utilisateur avec PushManager

Une fois notre service worker enregistré et que nous avons obtenu l'autorisation, nous pouvons abonner un utilisateur en appelant registration.pushManager.subscribe().

function subscribeUserToPush() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      const subscribeOptions = {
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
          'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
        ),
      };

      return registration.pushManager.subscribe(subscribeOptions);
    })
    .then(function (pushSubscription) {
      console.log(
        'Received PushSubscription: ',
        JSON.stringify(pushSubscription),
      );
      return pushSubscription;
    });
}

Lorsque vous appelez la méthode subscribe(), vous transmettez un objet options, qui se compose de paramètres obligatoires et facultatifs.

Examinons toutes les options que nous pouvons transmettre.

Options userVisibleOnly

Lorsque le push a été ajouté pour la première fois aux navigateurs, il n'était pas certain que les développeurs puissent envoyer un message push sans afficher de notification. On parle généralement de push silencieux, car l'utilisateur ne sait pas qu'un événement s'est produit en arrière-plan.

Le problème était que les développeurs pouvaient faire des choses malveillantes, comme suivre en permanence la position d'un utilisateur à son insu.

Pour éviter ce scénario et laisser aux auteurs de la spécification le temps de réfléchir à la meilleure façon de prendre en charge cette fonctionnalité, l'option userVisibleOnly a été ajoutée. Transmettre une valeur de true constitue un accord symbolique avec le navigateur selon lequel l'application Web affichera une notification chaque fois qu'un push sera reçu (c'est-à-dire qu'il n'y aura pas de push silencieux).

Pour le moment, vous devez transmettre une valeur de true. Si vous n'incluez pas la clé userVisibleOnly ou ne transmettez pas false, l'erreur suivante s'affiche:

Chrome n'est actuellement compatible qu'avec l'API Push pour les abonnements qui génèrent des messages visibles par l'utilisateur. Pour ce faire, appelez pushManager.subscribe({userVisibleOnly: true}) à la place. Pour en savoir plus, consultez la page https://goo.gl/yqv4Q4.

Il semble actuellement que les notifications push silencieuses ne seront jamais implémentées dans Chrome. Au lieu de cela, les auteurs de spécifications explorent la notion d'API de budget qui permettra aux applications Web d'envoyer un certain nombre de messages push silencieux en fonction de l'utilisation d'une application Web.

Option applicationServerKey

Nous avons brièvement mentionné les "clés de serveur d'application" dans la section précédente. Les "clés de serveur d'application" sont utilisées par un service push pour identifier l'application qui abonne un utilisateur et s'assurer que la même application envoie des messages à cet utilisateur.

Les clés de serveur d'application sont une paire de clés publique et privée propre à votre application. La clé privée doit rester secrète pour votre application, et la clé publique peut être partagée librement.

L'option applicationServerKey transmise à l'appel subscribe() correspond à la clé publique de l'application. Le navigateur transmet cette information à un service push lors de l'abonnement de l'utilisateur. Cela signifie que le service push peut associer la clé publique de votre application à l'PushSubscription de l'utilisateur.

Le schéma ci-dessous illustre ces étapes.

  1. Votre application Web est chargée dans un navigateur et vous appelez subscribe(), en transmettant votre clé publique du serveur d'application.
  2. Le navigateur envoie ensuite une requête réseau à un service de transfert qui génère un point de terminaison, l'associe à la clé publique de l'application et le renvoie au navigateur.
  3. Le navigateur ajoutera ce point de terminaison à PushSubscription, qui est renvoyé via la promesse subscribe().

Illustration de la clé publique du serveur d'application utilisée dans la méthode subscribe.

Lorsque vous souhaitez envoyer un message push ultérieurement, vous devez créer un en-tête Authorization (Autorisation) qui contiendra des informations signées avec la clé privée de votre serveur d'application. Lorsque le service push reçoit une requête d'envoi d'un message push, il peut valider cet en-tête Authorization signé en recherchant la clé publique associée au point de terminaison qui reçoit la requête. Si la signature est valide, le service de transfert par poussée sait qu'elle doit provenir du serveur d'application avec la clé privée correspondante. Il s'agit essentiellement d'une mesure de sécurité qui empêche toute autre personne d'envoyer des messages aux utilisateurs d'une application.

Utilisation de la clé privée du serveur d'application lors de l'envoi d'un message

Techniquement, applicationServerKey est facultatif. Toutefois, l'implémentation la plus simple sur Chrome l'exige, et d'autres navigateurs pourraient en avoir besoin à l'avenir. Cette étape est facultative dans Firefox.

La spécification qui définit ce que doit être la clé de serveur d'application est la spécification VAPID. Chaque fois que vous lisez quelque chose qui fait référence à des "clés de serveur d'application" ou à des "clés VAPID", n'oubliez pas qu'il s'agit de la même chose.

Créer des clés de serveur d'application

Vous pouvez créer un ensemble public et privé de clés de serveur d'application en accédant à web-push-codelab.glitch.me ou en utilisant la ligne de commande web-push pour générer des clés en procédant comme suit:

    $ npm install -g web-push
    $ web-push generate-vapid-keys

Vous n'avez besoin de créer ces clés qu'une seule fois pour votre application. Veillez simplement à conserver la clé privée de manière privée. (Oui, je viens de le dire.)

Autorisations et subscribe()

L'appel de subscribe() a un effet secondaire. Si votre application Web ne dispose pas des autorisations nécessaires pour afficher des notifications au moment de l'appel de subscribe(), le navigateur les demandera à votre place. Cela est utile si votre UI fonctionne avec ce flux, mais si vous souhaitez plus de contrôle (et je pense que la plupart des développeurs le feront), tenez-vous-en à l'API Notification.requestPermission() que nous avons utilisée précédemment.

Qu'est-ce qu'un PushSubscription ?

Nous appelons subscribe(), transmettons des options et, en retour, nous obtenons une promesse qui se résout en PushSubscription, ce qui génère un code semblable à celui-ci:

function subscribeUserToPush() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      const subscribeOptions = {
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
          'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
        ),
      };

      return registration.pushManager.subscribe(subscribeOptions);
    })
    .then(function (pushSubscription) {
      console.log(
        'Received PushSubscription: ',
        JSON.stringify(pushSubscription),
      );
      return pushSubscription;
    });
}

L'objet PushSubscription contient toutes les informations requises pour envoyer un message push à cet utilisateur. Si vous imprimez le contenu à l'aide de JSON.stringify(), vous verrez ce qui suit:

    {
      "endpoint": "https://some.pushservice.com/something-unique",
      "keys": {
        "p256dh":
    "BIPUL12DLfytvTajnryr2PRdAgXS3HGKiLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WAkAPIxr4gK0_dQds4yiI=",
        "auth":"FPssNDTKnInHVndSTdbKFw=="
      }
    }

endpoint est l'URL des services push. Pour déclencher un message push, envoyez une requête POST à cette URL.

L'objet keys contient les valeurs utilisées pour chiffrer les données de message envoyées avec un message push (que nous verrons plus loin dans cette section).

Réabonnement régulier pour éviter l'expiration

Lorsque vous vous abonnez aux notifications push, vous recevez souvent une PushSubscription.expirationTime de null. En théorie, cela signifie que l'abonnement n'expire jamais (contrairement à ce qui se passe lorsque vous recevez un DOMHighResTimeStamp, qui indique le moment exact où l'abonnement expire). En pratique, cependant, il est courant que les navigateurs laissent les abonnements expirer, par exemple si aucune notification push n'a été reçue pendant une longue période ou si le navigateur détecte que l'utilisateur n'utilise pas d'application disposant de l'autorisation de notifications push. Pour éviter cela, vous pouvez réabonner l'utilisateur à chaque notification reçue, comme illustré dans l'extrait suivant. Pour ce faire, vous devez envoyer des notifications suffisamment fréquemment pour que le navigateur ne mette pas automatiquement fin à l'abonnement. Vous devez donc peser très soigneusement les avantages et les inconvénients des besoins légitimes en notifications par rapport au spam involontaire de l'utilisateur afin que l'abonnement ne prenne pas fin. En fin de compte, vous ne devez pas essayer de lutter contre le navigateur dans ses efforts pour protéger l'utilisateur des abonnements aux notifications oubliés depuis longtemps.

/* In the Service Worker. */

self.addEventListener('push', function(event) {
  console.log('Received a push message', event);

  // Display notification or handle data
  // Example: show a notification
  const title = 'New Notification';
  const body = 'You have new updates!';
  const icon = '/images/icon.png';
  const tag = 'simple-push-demo-notification-tag';

  event.waitUntil(
    self.registration.showNotification(title, {
      body: body,
      icon: icon,
      tag: tag
    })
  );

  // Attempt to resubscribe after receiving a notification
  event.waitUntil(resubscribeToPush());
});

function resubscribeToPush() {
  return self.registration.pushManager.getSubscription()
    .then(function(subscription) {
      if (subscription) {
        return subscription.unsubscribe();
      }
    })
    .then(function() {
      return self.registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
      });
    })
    .then(function(subscription) {
      console.log('Resubscribed to push notifications:', subscription);
      // Optionally, send new subscription details to your server
    })
    .catch(function(error) {
      console.error('Failed to resubscribe:', error);
    });
}

Envoyer un abonnement à votre serveur

Une fois que vous avez un abonnement push, vous devez l'envoyer à votre serveur. C'est à vous de choisir comment procéder, mais nous vous conseillons d'utiliser JSON.stringify() pour extraire toutes les données nécessaires de l'objet d'abonnement. Vous pouvez également obtenir le même résultat manuellement comme suit:

const subscriptionObject = {
  endpoint: pushSubscription.endpoint,
  keys: {
    p256dh: pushSubscription.getKeys('p256dh'),
    auth: pushSubscription.getKeys('auth'),
  },
};

// The above is the same output as:

const subscriptionObjectToo = JSON.stringify(pushSubscription);

L'envoi de l'abonnement s'effectue sur la page Web comme suit:

function sendSubscriptionToBackEnd(subscription) {
  return fetch('/api/save-subscription/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(subscription),
  })
    .then(function (response) {
      if (!response.ok) {
        throw new Error('Bad status code from server.');
      }

      return response.json();
    })
    .then(function (responseData) {
      if (!(responseData.data && responseData.data.success)) {
        throw new Error('Bad response from server.');
      }
    });
}

Le serveur de nœud reçoit cette requête et enregistre les données dans une base de données pour les utiliser ultérieurement.

app.post('/api/save-subscription/', function (req, res) {
  if (!isValidSaveRequest(req, res)) {
    return;
  }

  return saveSubscriptionToDatabase(req.body)
    .then(function (subscriptionId) {
      res.setHeader('Content-Type', 'application/json');
      res.send(JSON.stringify({data: {success: true}}));
    })
    .catch(function (err) {
      res.status(500);
      res.setHeader('Content-Type', 'application/json');
      res.send(
        JSON.stringify({
          error: {
            id: 'unable-to-save-subscription',
            message:
              'The subscription was received but we were unable to save it to our database.',
          },
        }),
      );
    });
});

Avec les informations PushSubscription sur notre serveur, nous pouvons envoyer un message à notre utilisateur à tout moment.

Réabonnement régulier pour éviter l'expiration

Lorsque vous vous abonnez aux notifications push, vous recevez souvent une PushSubscription.expirationTime de null. En théorie, cela signifie que l'abonnement n'expire jamais (contrairement à ce qui se passe lorsque vous recevez un DOMHighResTimeStamp, qui indique le moment exact où l'abonnement expire). En pratique, cependant, il est courant que les navigateurs laissent les abonnements expirer, par exemple si aucune notification push n'a été reçue pendant une longue période ou si le navigateur détecte que l'utilisateur n'utilise pas l'application disposant de l'autorisation de notifications push. Pour éviter cela, vous pouvez réabonner l'utilisateur à chaque notification reçue, comme illustré dans l'extrait suivant. Pour ce faire, vous devez envoyer des notifications suffisamment fréquemment pour que le navigateur ne mette pas automatiquement fin à l'abonnement. Vous devez donc peser très soigneusement les avantages et les inconvénients des besoins légitimes de notification par rapport au spam de l'utilisateur afin que l'abonnement ne prenne pas fin. En fin de compte, vous ne devez pas essayer de lutter contre le navigateur dans ses efforts pour protéger l'utilisateur des abonnements aux notifications oubliés depuis longtemps.

/* In the Service Worker. */

self.addEventListener('push', function(event) {
  console.log('Received a push message', event);

  // Display notification or handle data
  // Example: show a notification
  const title = 'New Notification';
  const body = 'You have new updates!';
  const icon = '/images/icon.png';
  const tag = 'simple-push-demo-notification-tag';

  event.waitUntil(
    self.registration.showNotification(title, {
      body: body,
      icon: icon,
      tag: tag
    })
  );

  // Attempt to resubscribe after receiving a notification
  event.waitUntil(resubscribeToPush());
});

function resubscribeToPush() {
  return self.registration.pushManager.getSubscription()
    .then(function(subscription) {
      if (subscription) {
        return subscription.unsubscribe();
      }
    })
    .then(function() {
      return self.registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
      });
    })
    .then(function(subscription) {
      console.log('Resubscribed to push notifications:', subscription);
      // Optionally, send new subscription details to your server
    })
    .catch(function(error) {
      console.error('Failed to resubscribe:', error);
    });
}

Questions fréquentes

Voici quelques questions courantes que les utilisateurs se posent à ce stade:

Puis-je modifier le service push utilisé par un navigateur ?

Non. Le service de notification push est sélectionné par le navigateur et, comme nous l'avons vu avec l'appel subscribe(), le navigateur envoie des requêtes réseau au service de notification push pour récupérer les détails qui constituent le PushSubscription.

Chaque navigateur utilise un service de transfert de données différent. N'ont-ils pas des API différentes ?

Tous les services de push s'attendent à la même API.

Cette API courante est appelée Web Push Protocol et décrit la requête réseau que votre application doit effectuer pour déclencher un message push.

Si j'abonne un utilisateur sur son ordinateur, est-il également abonné sur son téléphone ?

Malheureusement, non. Un utilisateur doit s'inscrire pour les notifications push sur chaque navigateur sur lequel il souhaite recevoir des messages. Notez également que l'utilisateur devra accorder l'autorisation sur chaque appareil.

Étapes suivantes

Ateliers de programmation