Publié par

Il y a 4 mois -

Temps de lecture 7 minutes

Ajouter des notifications sur une app Web : Firebase Messaging

Par le biais des services workers et de la Push API, il est maintenant possible de recevoir des notifications sur des applications Web directement dans le navigateur même si l’application (Web) n’est pas ouverte.

Dans cet article nous allons intégrer les notifications à une simple page web via Firebase Messaging.

Prérequis

Les notifications ne fonctionneront que si notre navigateur supporte la Push API, nous pouvons nous référer à la matrice de compatibilité de la Push API.

Comme nous utilisons Firebase, il faudra commencer par créer un compte Firebase et ajouter Firebase à notre projet.

Nous allons effectuer des appels HTTP pour envoyer les notifications, nous aurons donc besoin d’un client HTTP, Insomnia est l’un d’entre eux.

Configurer Firebase

Au sein du projet Firebase, il faut récupérer certaines informations :

1. Parameters > Project settings > Cloud Messaging > Web Configuration (en bas) > Generate key pair

Cette key pair est à utiliser pour configurer Firebase :

firebase.messaging().usePublicVapidKey("BKagOny0KF_2pCJQ3m....moL0ewzQ8rZu");

2. Dans le fichier manifest.json (à créer si vous n’en avez pas déjà un), ajoutez ce numéro (identique pour toutes les applications, permet à Firebase Cloud Messaging (FCM) d’envoyer des messages à cette application).

{
  "gcm_sender_id": "103953800507"
}

3. Créer un fichier appelé firebase-messaging-sw.js à la racine de l’application. Ce fichier correspond au service worker dédié aux notifications Firebase. Nous le compléterons plus tard.

Un service worker correspond aux fondations techniques permettant à une application Web de proposer une expérience hors-ligne complète, synchronisation en tâche de fond, notifications, etc. habituellement réservée aux applications natives.

Permission et token

Pour que le navigateur puisse afficher des notifications, il faut que l’utilisateur l’autorise. Pour cela il est nécessaire d’appeler la méthode requestPermission() :

async function requestPermission() {
  const permission = await Notification.requestPermission();
  if (permission === 'granted') {
    // 🚀
  } else {
    // 😢
  }
}
requestPermission();

Une fois la permission accordée, nous pouvons récupérer le token Firebase Messaging :

async function requestPermission() {
  ...
  if (permission === 'granted') {
    getToken();
  } else {
    ...
  }
}

async function getToken() {
  const token = await firebase.messaging().getToken();
  if (token) {
    // 🚀
  }
}

Le token peut changer durant la vie de l’application, il est donc recommandé d’utiliser également la méthode onTokenRefresh() et d’effectuer le même traitement que dans la méthode getToken()

Recevoir une notification

Il y a deux types de notifications :

  1. celles de type données pour mettre à jour l’état d’une application (noeud data)
  2. celles de type message pour informer l’utilisateur (noeud notification)

Dans les deux cas, la notification peut être reçue lorsque l’application est active et également lorsqu’elle ne l’est pas (en utilisant les services workers), les exemples ci-dessous correspondent à des notifications de type message :

Quand l’application est active

Pour recevoir une notification quand l’application est active nous devons utiliser la méthode onMessage() de Firebase :

messaging.onMessage((payload) => {
  //
});

Sans cette méthode, si une notification est envoyée et que l’utilisateur navigue sur l’application, il ne se passera rien.

Quand l’application est inactive (en background ou fermée)

L’application n’étant pas visible, la fonction onMessage n’est pas appelée à la réception d’une notification, pour permettre la réception d’une notification et avoir un traitement personnalisé, il faut utiliser le service worker firebase-messaging-sw.js :

importScripts('https://www.gstatic.com/firebasejs/6.1.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/6.1.1/firebase-messaging.js');

firebase.initializeApp({
  messagingSenderId: '{{ sender_id }}',
});

Remarquez que, même si nous n’avons pas codé l’affichage de la notification, celle-ci est tout de même affichée par le navigateur, c’est le SDK qui gère cela.

importScripts provient de WorkerGlobalScope.importScripts() et permet l’import synchrone des scripts dans le contexte du service worker.

Il est possible d’utiliser la méthode setBackgroundMessageHandler(), dans le fichier firebase-messaging-sw.js, lorsqu’une notification est reçue à condition de ne pas avoir de noeud notification dans le payload de cette dernière. Cela peut être utilisé pour effectuer un traitement avant d’afficher une notification par exemple.

...
messaging.setBackgroundMessageHandler((payload) => {
  const title = 'Hello world!';
  const notificationOptions = {
    body: 'Ping?',
    icon: '/512.png'
  };

  return self.registration.showNotification(notificationTitle,
    notificationOptions);
});

Envoyer une notification

Envoyer une notification pour une application Web n’est pas (pour le moment) possible depuis la console Firebase (iOS et Android seulement). Il va donc falloir utiliser un client HTTP. Sur Insomnia ça se passe de la manière suivante :

⚠️Vérifier qu’une adresse email de contact est bien renseignée dans les paramètres généraux de l’application sur Firebase (permet l’affichage de la page de consentement pour l’OAuth 2).

Récupérer la configuration de l’application Firebase : Google Developer Console > API & Services > Credentials > OAuth 2 client IDs > Download JSON

https://console.developers.google.com/apis/credentials?folder=&organizationId=&project={{ project_id }}

Dans Inmonia :

Créer une nouvelle requête POST : https://fcm.googleapis.com/v1/projects/{{ project_id }}/messages:send

OAuth 2 :

Authorization URL : auth_uri

Access Token URL : token_uri

Client ID : client_id

Client Secret : client_secret

Redirect URL : redirect_uris (prendre une valeur du tableau)

Dans le menu des options avancées :

Scope : https://www.googleapis.com/auth/firebase.messaging

Pour le body (JSON) le payload minimum est :

{
  "message": {
	"token": "cBMzeUJcP...kkcStvlvM",
	"notification": {
  	  "body": "Ping?",
      "title": "Hello world!"
	}
  }
}

Pour le moment, seules les notifications envoyées alors que l’application est en arrière-plan ou inactive seront affichées (via le service worker firebase-messaging-sw.js)

Personnaliser les notifications

Les notifications envoyées, jusqu’à maintenant, ne contiennent qu’un titre et une description (et une icône dans le cas d’une notification dans le service worker). Une notification peut contenir plus d’informations comme un logo d’application, une action au clic, des actions, etc.

Pour bénéficier des notifications enrichies, nous utilisons la Web Notification API ainsi que la WebpushConfig de Firebase :

{
  "message": {
    "token": "{{ token }}",
	"notification": { ... },
	"webpush": {
	  "notification": {
		"body": "Ping?",
		"title": "Hello world!",
		"icon": "/512.png",
		"badge": "/notification.png"
	  },
	  "fcm_options": {
	    "link": "{{ your website }}"
	  }
	}
  }
}

Ce payload envoie une notification qui contient une notification webpush avec :

  • body : un corps de message
  • title : un titre
  • icon : un logo d’application
  • badge : un icône dans la barre Android / iOS (nécessite un logo monochrome blanc sur fond transparent)
  • fcm_options : une action au clic sur la notification (ouverture de l’URL dans le champ link)

Envoyer des notifications à un topic (plusieurs utilisateurs)

Il est bien sûr possible de faire une boucle sur les tokens pour envoyer la même notification à plusieurs utilisateurs. Néanmoins, Firebase propose une autre solution plus efficace : les topics.

Dès qu’un token est récupéré côté navigateur client, celui-ci est envoyé à un backend (par exemple une Firebase Functions) pour l’ajouter dans le topic correspondant :

export const subscribeToTopic = functions.https.onRequest(async (request, response) => {
  try {
    const subResponse = await admin.messaging()
      .subscribeToTopic([request.body.registration_token], request.body.topic_name);
    response.send({ successCount: subResponse.successCount });
  } catch (e) {
    response.redirect(500, e.toString());
  }
});

Dans la fonction ci-dessus le token et le topic sont passés en body POST de l’appel. Firebase Admin permet ensuite via la méthode subscribeToTopic() de lier le token au topic.

De cette manière en un appel (toujours via une Firebase Function) il est possible de notifier tous les tokens d’un topic :

export const subscribeToTopic = functions.https.onRequest(async (request, response) => {
  try {
    const res = await admin.messaging().send({
      topic: request.body.topic_name,
      notification: {
        body: request.body.message.body,
        title: request.body.message.title
      },
      webpush: {
        notification: {
          body: request.body.message.body,
          title: request.body.message.title,
          icon: '/img/icons/favicon-196x196.png',
          badge: '/img/notification.png'
        },
        fcm_options: {
          link: request.body.message.link
        }
      }
    });
    response.send({ res });
  } catch (e) {
    response.redirect(500, e.toString());
  }
});

Cet appel peut également être fait via le client REST déjà utilisé en remplaçant le champ token par un champ topic et en renseignant le bon topic.

Take away

Nous savons maintenant qu’il est possible d’envoyer, et de recevoir, des notifications sur les navigateurs compatibles, que l’application soit active ou non. Firebase simplifie grandement la gestion des notifications côté client et serveur.

Retrouvez tous les exemples de code dans le projet MemoDay :

Ainsi que la documentation Firebase sur le sujet :

Publié par

Publié par Benjamin Lacroix

Benjamin Lacroix est lead développeur et manager chez Xebia. Il est également formateur au sein de Xebia Training .

Commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.