Suscribe a un usuario a las notificaciones push

Para enviar mensajes push, primero debes obtener el permiso del usuario y, luego, suscribir su dispositivo a un servicio push. Esto implica usar la API de JavaScript para obtener un objeto PushSubscription, que luego envías a tu servidor.

La API de JavaScript administra este proceso de forma sencilla. En esta guía, se explica todo el flujo, incluida la detección de funciones, la solicitud de permiso y la administración del proceso de suscripción.

Detección de características

Primero, verifica si el navegador admite mensajes push. Puedes verificar la compatibilidad con las notificaciones push de dos maneras:

  • Verifica si hay serviceWorker en el objeto navigator.
  • Verifica si hay PushManager en el objeto 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;
}

Si bien el soporte del navegador para el trabajador de servicio y la mensajería push está aumentando, siempre detecta ambas funciones y mejora tu aplicación de forma progresiva.

Cómo registrar un service worker

Después de la detección de funciones, sabrás que se admiten los Service Workers y la mensajería push. A continuación, registra tu trabajador de servicio.

Cuando registras un service worker, le indicas al navegador la ubicación de tu archivo de service worker. El archivo es un archivo JavaScript, pero el navegador le otorga acceso a las APIs de Service Worker, incluida la mensajería push. Específicamente, el navegador ejecuta el archivo en un entorno de Service Worker.

Para registrar un service worker, llama a navigator.serviceWorker.register() y pasa la ruta de acceso a tu archivo. Por ejemplo:

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

Esta función le indica al navegador la ubicación de tu archivo de Service Worker. Aquí, el archivo del trabajador de servicio se encuentra en /service-worker.js. Después de llamar a register(), el navegador realiza los siguientes pasos:

  1. Descarga el archivo del service worker.

  2. Ejecuta el código JavaScript.

  3. Si el archivo se ejecuta correctamente sin errores, se resuelve la promesa que devuelve register(). Si se producen errores, se rechaza la promesa.

Nota: Si register() rechaza la solicitud, verifica si hay errores o errores de escritura en tu código JavaScript en las Herramientas para desarrolladores de Chrome.

Cuando register() se resuelve, devuelve un ServiceWorkerRegistration. Usas este registro para acceder a la API de PushManager.

Compatibilidad del navegador con la API de PushManager

Browser Support

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

Source

Cómo solicitar permiso

Después de registrar tu service worker y obtener el permiso, pídele al usuario permiso para enviar mensajes push.

La API para obtener permiso es simple. Sin embargo, recientemente, la API dejó de tomar una devolución de llamada para devolver una promesa. Como no puedes determinar qué versión de la API implementa el navegador, debes implementar y controlar ambas versiones.

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.");
    }
  });
}

En el código anterior, la llamada a Notification.requestPermission() muestra un mensaje al usuario:

Mensaje de permiso que se muestra en Chrome para computadoras y dispositivos móviles.

Después de que el usuario interactúa con el mensaje de permiso seleccionando Permitir, Bloquear o cerrándolo, recibes el resultado como una cadena: 'granted', 'default' o 'denied'.

En el código de muestra, la promesa que devuelve askPermission() se resuelve si se otorga el permiso; de lo contrario, arroja un error y se rechaza la promesa.

Controla el caso límite en el que el usuario hace clic en el botón Bloquear. Si esto ocurre, tu app web no podrá volver a solicitarle permiso al usuario. El usuario debe desbloquear manualmente tu app cambiando el estado de permiso en un panel de configuración. Considera cuidadosamente cuándo y cómo solicitar permiso, ya que, si un usuario hace clic en Bloquear, no será fácil revertir esa decisión.

La mayoría de los usuarios otorgan el permiso si comprenden por qué la app lo solicita.

En este documento, se explica cómo algunos sitios populares solicitan permiso.

Suscribe a un usuario con PushManager

Después de registrar tu service worker y obtener el permiso, puedes suscribir a un usuario llamando a 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;
    });
}

Cuando llamas al método subscribe(), pasas un objeto options que consta de parámetros obligatorios y opcionales.

En esta sección, se describen las opciones que puedes pasar.

Opciones de userVisibleOnly

Cuando se agregaron los mensajes push a los navegadores, los desarrolladores no tenían certeza sobre el envío de mensajes push sin mostrar una notificación. Esto se conoce comúnmente como envío silencioso porque el usuario no sabe que ocurrió un evento en segundo plano.

La preocupación era que los desarrolladores pudieran hacer un seguimiento continuo de la ubicación de un usuario sin su conocimiento.

Para evitar esta situación y permitir que los autores de especificaciones consideren la mejor manera de admitir esta función, se agregó la opción userVisibleOnly. Pasar un valor de true es un acuerdo simbólico con el navegador de que la app web muestra una notificación cada vez que recibe un mensaje push (es decir, no hay push silencioso).

Debes pasar un valor de true. Si no incluyes la clave userVisibleOnly o no pasas false, recibirás el siguiente error:

Chrome currently only supports the Push API for subscriptions that will result
in user-visible messages. You can indicate this by calling
`pushManager.subscribe({userVisibleOnly: true})` instead. See
[https://goo.gl/yqv4Q4](https://goo.gl/yqv4Q4) for more details.

Chrome solo admite la API de Push para las suscripciones que generan mensajes visibles para el usuario. Para indicarlo, llama a pushManager.subscribe({userVisibleOnly: true}). Para obtener más información, consulta https://goo.gl/yqv4Q4.

Al parecer, no se implementará la notificación push silenciosa general en Chrome. En cambio, los autores de especificaciones están explorando una API de presupuesto que permite que las apps web envíen una cierta cantidad de mensajes push silenciosos según el uso de la app web.

Opción applicationServerKey

Anteriormente, en este documento se mencionaban las claves del servidor de aplicaciones. Un servicio de envío usa claves del servidor de aplicaciones para identificar la aplicación que suscribe a un usuario y garantizar que la misma aplicación le envíe mensajes.

Las claves del servidor de aplicaciones son un par de claves públicas y privadas que son únicas para tu aplicación. Mantén la clave privada secreta para tu aplicación y comparte la clave pública libremente.

La opción applicationServerKey que se pasa en la llamada subscribe() es la clave pública de tu aplicación. El navegador pasa esta clave a un servicio push cuando se suscribe al usuario, lo que permite que el servicio push vincule la clave pública de tu aplicación al PushSubscription del usuario.

En el siguiente diagrama, se ilustran estos pasos.

  1. Carga tu app web en un navegador y llama a subscribe(), pasando la clave pública del servidor de aplicaciones.
  2. Luego, el navegador realiza una solicitud de red a un servicio push, que genera un extremo, asocia este extremo con la clave pública de tu aplicación y devuelve el extremo al navegador.
  3. El navegador agrega este extremo a PushSubscription, que devuelve la promesa subscribe().

Diagrama que ilustra cómo se usa la clave pública del servidor de aplicaciones en el método `subscribe()`.

Cuando envíes un mensaje push, crea un encabezado Authorization que contenga información firmada con la clave privada del servidor de tu aplicación. Cuando el servicio de envío recibe una solicitud para enviar un mensaje push, valida este encabezado Authorization firmado buscando la clave pública vinculada al extremo que recibe la solicitud. Si la firma es válida, el servicio de envío sabe que la solicitud provino del servidor de aplicaciones con la clave privada correspondiente. Esta es una medida de seguridad que impide que otras personas envíen mensajes a los usuarios de tu aplicación.

Diagrama que ilustra cómo se usa la clave privada del servidor de aplicaciones cuando se envía un mensaje.

Técnicamente, el applicationServerKey es opcional. Sin embargo, la implementación más simple en Chrome lo requiere, y es posible que otros navegadores lo requieran en el futuro. Es opcional en Firefox.

La especificación de VAPID define la clave del servidor de aplicaciones. Cuando veas referencias a claves del servidor de aplicaciones o claves de VAPID, recuerda que son lo mismo.

Crea claves del servidor de aplicaciones

Puedes crear un conjunto público y privado de claves del servidor de aplicaciones. Para ello, visita web-push-codelab.glitch.me o usa la línea de comandos de web-push para generar claves de la siguiente manera:

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

Crea estas claves solo una vez para tu aplicación y asegúrate de mantener la clave privada como privada.

Permisos y subscribe()

Llamar a subscribe() tiene un efecto secundario. Si tu app web no tiene permiso para mostrar notificaciones cuando llamas a subscribe(), el navegador solicitará los permisos por ti. Esto es útil si tu IU funciona con este flujo, pero si deseas tener más control (como la mayoría de los desarrolladores), usa la API de Notification.requestPermission() que se analizó anteriormente en este documento.

Descripción general de PushSubscription

Llamas a subscribe(), pasas opciones y recibes una promesa que se resuelve en un PushSubscription. Por ejemplo:

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

El objeto PushSubscription contiene toda la información necesaria para enviar mensajes push a ese usuario. Si imprimes el contenido con JSON.stringify(), verás lo siguiente:

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

endpoint es la URL del servicio de envío. Para activar un mensaje push, realiza una solicitud POST a esta URL.

El objeto keys contiene los valores que se usan para encriptar los datos de los mensajes enviados con un mensaje push. (En este documento, se analiza la encriptación de mensajes más adelante).

Envía una suscripción a tu servidor

Una vez que tengas una suscripción push, envíala a tu servidor. Tú decides cómo enviarlo, pero te recomendamos que uses JSON.stringify() para extraer todos los datos necesarios del objeto de suscripción. Como alternativa, puedes ensamblar manualmente el mismo resultado, por ejemplo:

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

Para enviar la suscripción desde la página web, usa lo siguiente:

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

El servidor de Node.js recibe esta solicitud y guarda los datos en una base de datos para su uso posterior.

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

Con los detalles de PushSubscription en tu servidor, puedes enviarle un mensaje al usuario en cualquier momento.

Vuelve a suscribirte periódicamente para evitar el vencimiento

Cuando te suscribes a las notificaciones push, a menudo recibes un PushSubscription.expirationTime de null. En teoría, esto significa que la suscripción nunca vence. (En cambio, un DOMHighResTimeStamp indica la hora de vencimiento exacta). Sin embargo, en la práctica, los navegadores suelen permitir que caduquen las suscripciones. Por ejemplo, esto puede ocurrir si no se reciben notificaciones push durante un período prolongado o si el navegador detecta que el usuario no está usando una app que tiene permiso para enviar notificaciones push. Un patrón para evitar esto es volver a suscribir al usuario cada vez que recibe una notificación, como se muestra en el siguiente fragmento. Esto requiere que envíes notificaciones con la frecuencia suficiente para evitar que el navegador haga que la suscripción caduque automáticamente. Debes sopesar cuidadosamente las ventajas y desventajas de las necesidades de notificación legítimas en comparación con el envío involuntario de spam al usuario solo para evitar el vencimiento de la suscripción. En última instancia, no debes intentar eludir los esfuerzos del navegador para proteger al usuario de las suscripciones a notificaciones olvidadas hace mucho tiempo.

/* 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);
    });
}

Preguntas frecuentes

Estas son algunas preguntas frecuentes:

¿Se puede cambiar el servicio push que usa un navegador?

No, el navegador selecciona el servicio de envío de notificaciones push. Como se explicó en este documento con la llamada a subscribe(), el navegador realiza solicitudes de red al servicio de envío para recuperar los detalles que componen el PushSubscription.

¿Los diferentes servicios de envío de notificaciones push usan APIs diferentes?

Todos los servicios de envío esperan la misma API.

Esta API común, llamada protocolo de envío de notificaciones push web, describe la solicitud de red que realiza tu aplicación para activar un mensaje push.

Si suscribes a un usuario en su computadora, ¿también se suscribe en su teléfono?

No. El usuario debe registrarse para recibir mensajes push en cada navegador en el que quiera recibirlos. Esto también requiere que el usuario otorgue permiso en cada dispositivo.

Próximos pasos

Codelabs