Suscribir a un usuario

El primer paso es obtener el permiso del usuario para enviarle mensajes push y, luego, podemos obtener un PushSubscription.

La API de JavaScript que hace esto es bastante sencilla, así que sigamos el flujo lógico.

Detección de funciones

Primero, debemos comprobar si el navegador actual es realmente compatible con la mensajería push. Podemos comprobar si push es compatible con dos comprobaciones simples.

  1. Comprueba si hay serviceWorker en navigator.
  2. Busca PushManager en 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 la compatibilidad con el navegador está creciendo rápidamente tanto para los service worker como para los mensajes push, siempre es una buena idea detectar funciones para ambos y mejorarlas progresivamente.

Registra un service worker

Con la función de detección, sabemos que se admiten service worker y push. El siguiente paso es “registrar” nuestro service worker.

Cuando registramos un service worker, le estamos indicando al navegador dónde está nuestro archivo de service worker. El archivo sigue siendo solo JavaScript, pero el navegador le "otorgará acceso" a las APIs del service worker, incluido push. Para ser más exactos, 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 nuestro archivo. Así:

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 indica al navegador que tenemos un archivo de service worker y dónde se ubica. En este caso, el archivo del service worker se encuentra en /service-worker.js. En segundo plano, el navegador realiza los siguientes pasos después de llamar a register():

  1. Descarga el archivo del service worker.

  2. Ejecuta JavaScript.

  3. Si todo se ejecuta correctamente y no hay errores, se resolverá la promesa que muestra register(). Si hay errores de cualquier tipo, se rechazará la promesa.

Si register() se rechaza, comprueba que no haya errores tipográficos ni errores de JavaScript en las Herramientas para desarrolladores de Chrome.

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

Compatibilidad con el navegador de la API de PushManager

Navegadores compatibles

  • 42
  • 17
  • 44
  • 16

Origen

Cómo solicitar permiso

Hemos registrado nuestro service worker y estamos listos para suscribir al usuario. El siguiente paso es obtener el permiso del usuario para enviarle mensajes push.

La API para obtener el permiso es relativamente simple. La desventaja es que recientemente cambió de realizar una devolución de llamada a mostrar una promesa. El problema es que no podemos saber qué versión de la API implementa el navegador actual, por lo que debes implementar ambos y controlarlos.

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, el fragmento de código importante es la llamada a Notification.requestPermission(). Con este método, se mostrará un mensaje al usuario:

Mensaje de permiso en Chrome para computadoras y dispositivos móviles

Una vez que el usuario haya interactuado con la solicitud de permiso y presione la opción Permitir, Bloquear o simplemente cerrándola, obtendremos el resultado como una cadena: 'granted', 'default' o 'denied'.

En el código de muestra anterior, la promesa que muestra askPermission() se resuelve si se otorga el permiso. De lo contrario, se genera un error que impide el rechazo de la promesa.

Un caso límite que debes controlar es cuando el usuario hace clic en el botón "Bloquear". Si esto sucede, la app web no podrá volver a solicitar permiso al usuario. Tendrá que "desbloquear" tu app manualmente cambiando su estado de permiso, que está oculto en un panel de configuración. Piensa detenidamente cómo y cuándo le solicitas permiso al usuario, ya que, si hace clic en Bloquear, no es una manera fácil de revertir esa decisión.

La buena noticia es que a la mayoría de los usuarios les gusta dar permiso, siempre y cuando saben el motivo de su solicitud.

Más adelante veremos cómo algunos sitios populares solicitan permiso.

Suscribe a un usuario con PushManager

Una vez que tengamos registrado el service worker y tengamos permiso, podemos llamar a registration.pushManager.subscribe() para suscribir un usuario.

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 se llama al método subscribe(), se pasa un objeto options, que consta de parámetros obligatorios y opcionales.

Veamos todas las opciones que podemos pasar.

Opciones de userVisibleOnly

Cuando se agregó push por primera vez a los navegadores, no había certeza sobre si los desarrolladores podían enviar un mensaje push y no mostrar una notificación. Por lo general, esto se conoce como "push" silencioso, debido a que el usuario no sabía que algo había sucedido en segundo plano.

La preocupación era que los desarrolladores podían hacer cosas desagradables, como rastrear la ubicación de un usuario de forma continua sin que el usuario lo supiera.

Para evitar esta situación y dar tiempo a los autores de especificaciones para que consideren la mejor manera de admitir esta función, se agregó la opción userVisibleOnly, y pasar un valor de true es un acuerdo simbólico con el navegador de que la app web mostrará una notificación cada vez que se reciba un envío (es decir, no push silencio).

Por el momento, debes pasar un valor de true. Si no incluyes la clave userVisibleOnly o pasas false, verás el siguiente error:

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

Actualmente, parece que nunca se implementará el envío silencioso y general en Chrome. En cambio, los autores de las especificaciones están explorando la noción de una API de presupuesto que permitirá a las aplicaciones web una cierta cantidad de mensajes push silenciosos basados en el uso de una aplicación web.

Opción applicationServerKey

Mencionamos brevemente "claves para servidor de aplicaciones" en la sección anterior. Los servicios push usan las “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 exclusivas de tu aplicación. La clave privada debe guardarse en secreto para tu aplicación y la clave pública se puede compartir libremente.

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

En el siguiente diagrama, se ilustran estos pasos.

  1. La aplicación web se carga en un navegador, llamas a subscribe() y pasas la clave de servidor de la aplicación pública.
  2. Luego, el navegador realiza una solicitud de red a un servicio push que generará un extremo, lo asocia con la clave pública de la aplicación y lo muestra al navegador.
  3. El navegador agregará este extremo a PushSubscription, que se muestra a través de la promesa subscribe().

La ilustración de la clave de servidor de la aplicación pública se usa en el método de suscripción.

Cuando más adelante desees enviar un mensaje push, tendrás que crear un encabezado de 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, puede validar este encabezado Authorization firmado a través de la búsqueda de la clave pública vinculada al extremo que recibe la solicitud. Si la firma es válida, el servicio de envío sabe que debe provenir del servidor de aplicaciones con la clave privada coincidente. Básicamente, es una medida de seguridad que evita que otra persona envíe mensajes a los usuarios de una aplicación.

Cómo se usa la clave de servidor de la aplicación privada cuando se envía un mensaje

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

La especificación que define cuál debe ser la clave del servidor de aplicaciones es la especificación de VAPID. Cuando leas algo que haga referencia a "claves del servidor de aplicaciones" o "claves VAPID", recuerda que son lo mismo.

Cómo crear claves de servidor de aplicaciones

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

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

Solo necesitas crear estas claves una vez para tu aplicación, solo debes asegurarte de mantener privada la clave privada. (Sí, acabo de decir eso).

Permisos y subscription()

Hay un efecto secundario de llamar a subscribe(). Si tu app web no tiene permisos para mostrar notificaciones en el momento en que llamas a subscribe(), el navegador te los solicitará. Esto es útil si tu IU funciona con este flujo, pero si quieres tener más control (y creo que la mayoría de los desarrolladores lo harán), usa la API de Notification.requestPermission() que usamos antes.

¿Qué es una PushSubscription?

Llamamos a subscribe(), pasamos algunas opciones y, a cambio, obtenemos una promesa que se resuelve en una PushSubscription que da como resultado un código como el siguiente:

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 de los servicios de envío. Para activar un mensaje de envío, realiza una solicitud POST a esta URL.

El objeto keys contiene los valores que se usan para encriptar los datos de mensajes enviados con un mensaje push (que analizaremos más adelante en esta sección).

Enviar una suscripción a tu servidor

Una vez que tengas una suscripción de envío, querrás enviarla a tu servidor. Depende de ti cómo hacerlo, pero una pequeña sugerencia es usar JSON.stringify() para obtener todos los datos necesarios del objeto de suscripción. También puedes unir el mismo resultado de forma manual de la siguiente manera:

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

El envío de la suscripción se realiza en la página web de la siguiente manera:

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 nodos recibe esta solicitud y guarda los datos en una base de datos para usarlos más adelante.

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 nuestro servidor, podemos enviar un mensaje a nuestro usuario cuando lo deseemos.

Preguntas frecuentes

Algunas preguntas frecuentes que se han hecho hasta el momento:

¿Puedo cambiar el servicio push que usa un navegador?

No. El navegador selecciona el servicio de envío y, como vimos con la llamada a subscribe(), el navegador realizará solicitudes de red al servicio push para recuperar los detalles que conforman la PushSubscription.

Cada navegador usa un servicio push diferente, ¿no tienen diferentes APIs?

Todos los servicios de envío esperarán la misma API.

Esta API común se denomina protocolo de envío web y describe la solicitud de red que tu aplicación deberá realizar para activar un mensaje push.

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

Lamentablemente, no. El usuario debe registrarse para push en cada navegador en el que desea recibir mensajes. También vale la pena señalar que esto requerirá que el usuario otorgue permiso en cada dispositivo.

Próximos pasos

Code labs