Inscrição de um usuário

A primeira etapa é obter permissão do usuário para enviar mensagens push e, em seguida, podemos pegar um PushSubscription.

A API JavaScript para fazer isso é bem simples. Vamos analisar pelo fluxo lógico.

Detecção de recursos

Primeiro, precisamos verificar se o navegador atual realmente oferece suporte a mensagens push. Podemos verificar push é compatível com duas verificações simples.

  1. Procure serviceWorker em Navigator.
  2. Verifique se há PushManager na 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;
}

Embora o suporte aos navegadores esteja crescendo rapidamente, tanto para service workers quanto para mensagens push, é sempre uma boa ideia detectar atributos para e aprimorar progressivamente.

Registrar um service worker

Com a detecção de recurso, sabemos que tanto service workers quanto Push têm suporte. Próxima etapa é "registrar" nosso service worker.

Quando registramos um service worker, informamos ao navegador onde está o arquivo do service worker. O arquivo ainda é apenas JavaScript, mas o navegador "dá acesso" ao service worker APIs, incluindo push. Para ser mais exato, o navegador executa o arquivo em um service worker de nuvem.

Para registrar um service worker, chame navigator.serviceWorker.register(), transmitindo o caminho para em nosso arquivo. Assim:

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 função informa ao navegador que temos um arquivo do service worker e onde ele está localizado. Em Neste caso, o arquivo do service worker está em /service-worker.js. Nos bastidores do navegador seguirá as seguintes etapas depois de chamar register():

  1. Faça o download do arquivo do service worker.

  2. Execute o JavaScript.

  3. Se tudo funcionar corretamente e não houver erros, a promessa retornada por register() vai resolver. Se houver qualquer tipo de erro, a promessa vai ser rejeitada.

Se o register() for rejeitado, verifique novamente se há erros de digitação / erros de digitação no Chrome DevTools.

Quando register() é resolvido, ele retorna um ServiceWorkerRegistration. Vamos usar para acessar a API PushManager.

Compatibilidade com navegadores da API PushManager

Compatibilidade com navegadores

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

Origem

Solicitando permissão

Registramos nosso service worker e estamos prontos para fazer a inscrição do usuário. A próxima etapa é permissão do usuário para enviar mensagens push.

A API para obter permissão é relativamente simples. A desvantagem é que a API mudou recentemente de receber um callback para retornar uma promessa. A problema com isso, é que não podemos saber qual versão da API é implementada pela rede navegador. Portanto, você precisa implementar ambos e lidar com ambos.

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

No código acima, o snippet de código importante é a chamada para Notification.requestPermission(): Esse método exibirá uma solicitação ao usuário:

Solicitação de permissão no Chrome para computadores e dispositivos móveis.

Quando o usuário interage com a solicitação de permissão pressionando "Permitir", "Bloquear" ou simplesmente fechando-a, receberemos o resultado como uma string: 'granted', 'default' ou 'denied'.

No exemplo de código acima, a promessa retornada por askPermission() é resolvida se a permissão é concedido, caso contrário, geramos um erro fazendo a promessa ser rejeitada.

Um caso extremo que você precisa resolver é se o usuário clicar no botão "Bloquear" . Se esse acontecer, seu app da Web não poderá pedir a permissão do usuário novamente. Eles terão que "desbloquear" manualmente seu app mudando o estado de permissão, que é ocultado em um painel de configurações. Pense cuidadosamente em como e quando você pede permissão ao usuário, porque se ele clicar em "Bloquear", não será fácil reverter essa decisão.

A boa notícia é que a maioria dos usuários fica feliz em dar permissão, desde que ele sabe por que a permissão está sendo pedida.

Mais tarde veremos como alguns sites populares pedem permissão.

Inscrever um usuário com o PushManager

Depois de registrar o service worker e receber a permissão, podemos inscrever um usuário chamando 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;
    });
}

Ao chamar o método subscribe(), transmitimos um objeto options, que consiste em parâmetros obrigatórios e opcionais.

Vamos conferir todas as opções que podemos transmitir.

Opções userVisibleOnly

Quando o push foi adicionado pela primeira vez aos navegadores, havia uma incerteza sobre se os desenvolvedores deveriam enviar uma mensagem push e não mostrar uma notificação. Isso é chamado de silêncio push, porque o usuário não sabe que algo aconteceu em segundo plano.

A preocupação era que os desenvolvedores pudessem fazer coisas desagradáveis, como rastrear a localização de um usuário em um de forma contínua sem que o usuário saiba.

Para evitar esse cenário e permitir que os autores das especificações tenham tempo para considerar a melhor forma de oferecer suporte a isso a opção userVisibleOnly foi adicionada, e a transmissão de um valor de true é uma ação simbólica um acordo com o navegador de que o aplicativo da web mostrará uma notificação sempre que um push for recebidos (ou seja, sem envio silencioso).

No momento, é necessário transmitir um valor de true. Se você não incluir o parâmetro userVisibleOnly chave ou transmita false, você vai receber este erro:

No momento, o Chrome só é compatível com a API Push para assinaturas que resultarão em mensagens visíveis ao usuário. Você pode indicar isso chamando pushManager.subscribe({userVisibleOnly: true}). Consulte https://goo.gl/yqv4Q4 para mais detalhes.

No momento, parece que o envio silencioso geral nunca será implementado no Chrome. Em vez disso, os autores de especificações estão explorando a noção de uma API de orçamento que permitirá que aplicativos da Web que um determinado número de mensagens push silenciosas com base no uso de um app da Web.

Opção applicationServerKey

Mencionamos brevemente "chaves do servidor de aplicativos" na seção anterior. "O aplicativo chaves de servidor" são usados por um serviço push para identificar o aplicativo que está assinando um usuário garantir que o mesmo aplicativo esteja enviando mensagens para esse usuário.

As chaves do servidor de aplicativos são um par de chaves pública e privada exclusivo para seu aplicativo. A chave privada deve ser mantida em segredo para o aplicativo e a chave pública pode ser compartilhada livremente.

A opção applicationServerKey transmitida para a chamada subscribe() é o nome de usuário de dados. O navegador transmite isso para um serviço de push ao inscrever o usuário, o que significa que o pode vincular a chave pública do aplicativo ao PushSubscription do usuário.

O diagrama abaixo ilustra essas etapas.

  1. Seu app da Web é carregado em um navegador e você chama subscribe(), transmitindo seu app chave do servidor de aplicativos.
  2. Em seguida, o navegador faz uma solicitação de rede a um serviço de push que gerará um endpoint, associar o endpoint à chave pública do aplicativo e retorná-lo ao navegador.
  3. O navegador vai adicionar esse endpoint ao PushSubscription, que é retornado pelo subscribe().

Ilustração da chave pública do servidor de aplicativos sendo usada na assinatura
.

Quando quiser enviar uma mensagem push mais tarde, será necessário criar um cabeçalho Authorization que conterá informações assinadas com a chave privada do servidor do seu aplicativo. Quando o serviço de push recebe uma solicitação para enviar uma mensagem push, ele pode validar esse cabeçalho Authorization assinado procurando a chave pública vinculada ao endpoint que recebe a solicitação. Se a assinatura for válido o serviço de push sabe que deve ter vindo do servidor de aplicativos com a chave privada correspondente. É uma medida de segurança que impede que outra pessoa envie para os usuários de um aplicativo.

Como a chave privada do servidor de aplicativos é usada ao enviar uma
mensagem

Tecnicamente, o applicationServerKey é opcional. No entanto, a forma mais fácil implementação no Chrome precisa dela, e outros navegadores podem exigir isso em no futuro. Ele é opcional no Firefox.

A especificação que define qual deve ser a chave do servidor de aplicativos é as especificações VAPID. Sempre que você ler algo referente a "chaves do servidor de aplicativos" ou "Chaves VAPID", basta lembrar que elas são a mesma coisa.

Como criar chaves do servidor de aplicativos

É possível criar um conjunto público e privado de chaves do servidor de aplicativos acessando web-push-codelab.glitch.me ou o linha de comando web-push para gerar chaves fazendo o seguinte:

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

Você só precisa criar essas chaves uma vez para seu aplicativo. Basta manter privada da chave privada. (Sim, acabei de dizer isso.)

Permissões esubscribe()

Há um efeito colateral de chamar subscribe(). Caso seu app da Web não tenha permissões para mostrando notificações no momento de chamar subscribe(), o navegador solicitará o permissões para você. Isso é útil se sua interface funciona com esse fluxo, mas se você quiser mais (e acho que a maioria dos desenvolvedores irá), manter a API Notification.requestPermission() que usamos anteriormente.

O que é uma PushSubscription?

Chamamos subscribe(), transmitimos algumas opções e, em troca, recebemos uma promessa que se resolve em uma PushSubscription, resultando em um código como este:

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

O objeto PushSubscription contém todas as informações necessárias para enviar um push. mensagens para esse usuário. Se você imprimir o conteúdo usando JSON.stringify(), a seguinte:

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

O endpoint é o URL dos serviços de push. Para acionar uma mensagem push, faça uma solicitação POST a este URL.

O objeto keys contém os valores usados para criptografar dados de mensagens enviados com uma mensagem de push. Falaremos sobre isso mais tarde nesta seção.

Nova assinatura regular para evitar a expiração

Ao se inscrever para receber notificações push, você geralmente recebe um PushSubscription.expirationTime de null. Em teoria, isso significa que a assinatura nunca expira, ao contrário de quando você recebe uma DOMHighResTimeStamp, que informa o momento exato em que ela expira. Na prática, no entanto, é comum que os navegadores ainda deixem as assinaturas expirarem, por exemplo, se nenhuma notificação push for recebida por mais tempo ou se o navegador detectar que o usuário não está usando um app com a permissão de notificações push. Um padrão para evitar isso é inscrever o usuário novamente após cada notificação recebida, conforme mostrado no snippet a seguir. Isso exige que você envie notificações com frequência suficiente para que o navegador não expire automaticamente a assinatura. Você deve ponderar com muito cuidado as vantagens e desvantagens das necessidades de notificações legítimas em relação ao envio involuntário do usuário apenas para que a assinatura não expire. No final, você não deve tentar combater o navegador para proteger o usuário de inscrições há muito tempo esquecidas.

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

Enviar uma assinatura para seu servidor

Assim que tiver uma assinatura push, convém enviá-la para seu servidor. Depende de você como você fazer isso, mas uma pequena dica é usar JSON.stringify() para extrair todos os dados necessários objeto de assinatura. Como alternativa, é possível juntar resultado manualmente, assim:

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

O envio da inscrição é feito na página da Web da seguinte forma:

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

O servidor do nó recebe essa solicitação e salva os dados em um banco de dados para 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.',
          },
        }),
      );
    });
});

Com os detalhes de PushSubscription no servidor, podemos enviar ao usuário uma mensagem sempre que quisermos.

Nova assinatura regular para evitar a expiração

Ao se inscrever para receber notificações push, você geralmente recebe um PushSubscription.expirationTime de null. Em teoria, isso significa que a assinatura nunca expira, ao contrário de quando você recebe uma DOMHighResTimeStamp, que informa o momento exato em que ela expira. No entanto, na prática, é comum que os navegadores ainda deixem as assinaturas expirarem, por exemplo, se nenhuma notificação push for recebida por muito tempo ou se o navegador detectar que o usuário não está usando o app que tem a permissão de notificações push. Um padrão para evitar isso é inscrever o usuário novamente após cada notificação recebida, conforme mostrado no snippet a seguir. Isso exige que você envie notificações com frequência suficiente para que o navegador não expire automaticamente a assinatura. Você deve ponderar com muito cuidado as vantagens e desvantagens das necessidades de notificações legítimas em relação ao envio de spam ao usuário apenas para que a assinatura não expire. No final, você não deve tentar combater o navegador para proteger o usuário de inscrições há muito tempo esquecidas.

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

Perguntas frequentes

Algumas perguntas comuns que as pessoas têm feito neste momento:

Posso alterar o serviço de push usado por um navegador?

Não. O serviço push é selecionado pelo navegador e, como vimos nas subscribe(), o navegador fará solicitações de rede ao serviço de push para recuperar os detalhes que compõem a PushSubscription.

Cada navegador usa um serviço push diferente. Eles não têm APIs diferentes?

Todos os serviços de push esperam a mesma API.

Essa API comum é chamada de Protocolo push na Web e descreve a solicitação de rede que o aplicativo precisará fazer para acionar uma mensagem push.

Se eu inscrevo um usuário pelo computador, ele também será assinante pelo smartphone?

Infelizmente, não. Um usuário deve se registrar para push em cada navegador que quiser receber mensagens. Vale lembrar que isso exigirá o usuário concedendo permissão em cada dispositivo.

A seguir

Code labs