Inscrever um usuário para receber notificações push

Para enviar mensagens push, primeiro você precisa receber permissão do usuário e depois inscrever o dispositivo dele em um serviço push. Isso envolve o uso da API JavaScript para receber um objeto PushSubscription, que você envia para o servidor.

A API JavaScript gerencia esse processo de maneira simples. Este guia explica todo o fluxo, incluindo detecção de recursos, solicitação de permissão e gerenciamento do processo de assinatura.

Detecção de recursos

Primeiro, verifique se o navegador é compatível com mensagens push. É possível verificar o suporte a push com duas verificações:

  • Verifique se há serviceWorker no objeto navigator.
  • Verifique se há PushManager no 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;
}

Embora o suporte do navegador para service worker e mensagens push esteja aumentando, sempre detecte os dois recursos e melhore progressivamente seu aplicativo.

Registrar um service worker

Depois da detecção de recursos, você sabe que os service workers e as mensagens push são compatíveis. Em seguida, registre o service worker.

Ao registrar um service worker, você informa ao navegador o local do arquivo dele. O arquivo é JavaScript, mas o navegador concede acesso a APIs de service worker, incluindo mensagens push. Especificamente, o navegador executa o arquivo em um ambiente de service worker.

Para registrar um service worker, chame navigator.serviceWorker.register() e transmita o caminho para o arquivo. Exemplo:

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

Essa função informa ao navegador o local do arquivo do service worker. Aqui, o arquivo do service worker está em /service-worker.js. Depois de chamar register(), o navegador executa estas etapas:

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

  2. Execute o JavaScript.

  3. Se o arquivo for executado corretamente sem erros, a promessa retornada por register() será resolvida. Se ocorrerem erros, a promessa será rejeitada.

Observação: se register() rejeitar, verifique se há erros de digitação ou erros no JavaScript no Chrome DevTools.

Quando register() é resolvido, ele retorna um ServiceWorkerRegistration. Você usa esse registro para acessar a API PushManager.

Compatibilidade do navegador com a API PushManager

Browser Support

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

Source

Como solicitar permissão

Depois de registrar o service worker e receber permissão, peça autorização ao usuário para enviar mensagens push.

A API para receber permissão é simples. No entanto, a API mudou recentemente de receber um callback para retornar uma promessa. Como não é possível determinar qual versão da API o navegador implementa, é necessário implementar e processar as duas versões.

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 anterior, a chamada para Notification.requestPermission() mostra uma solicitação ao usuário:

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

Depois que o usuário interage com a solicitação de permissão selecionando Permitir, Bloquear ou fechando, você recebe o resultado como uma string: 'granted', 'default' ou 'denied'.

No código de amostra, a promessa retornada por askPermission() é resolvida se a permissão for concedida. Caso contrário, ela gera um erro e a promessa é rejeitada.

Trate o caso extremo em que o usuário clica no botão Bloquear. Se isso acontecer, seu web app não poderá pedir permissão ao usuário novamente. O usuário precisa desbloquear manualmente seu app mudando o estado de permissão dele em um painel de configurações. Considere com cuidado quando e como pedir permissão, porque se um usuário clicar em Bloquear, não será fácil reverter essa decisão.

A maioria dos usuários concede permissão se entender por que o app a solicita.

Neste documento, explicamos como alguns sites conhecidos pedem permissão.

Inscrever um usuário com o PushManager

Depois de registrar o service worker e receber permissão, chame registration.pushManager.subscribe() para inscrever um usuário.

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(), você transmite um objeto options que consiste em parâmetros obrigatórios e opcionais.

Esta seção descreve as opções que podem ser transmitidas.

Opções userVisibleOnly

Quando as mensagens push foram adicionadas aos navegadores, os desenvolvedores não tinham certeza sobre o envio de mensagens push sem exibir uma notificação. Isso é comumente chamado de push silencioso porque o usuário não sabe que um evento ocorreu em segundo plano.

A preocupação era que os desenvolvedores pudessem rastrear a localização de um usuário continuamente sem o conhecimento dele.

Para evitar esse cenário e permitir que os autores de especificações considerem a melhor forma de oferecer suporte a esse recurso, a opção userVisibleOnly foi adicionada. Transmitir um valor de true é um acordo simbólico com o navegador de que o app da Web vai mostrar uma notificação sempre que receber uma mensagem push (ou seja, sem push silencioso).

Você precisa transmitir um valor de true. Se você não incluir a chave userVisibleOnly ou transmitir false, vai receber o seguinte erro:

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.

O Chrome só oferece suporte à API Push para inscrições que resultam em mensagens visíveis para o usuário. Indique isso chamando pushManager.subscribe({userVisibleOnly: true}). Para mais informações, consulte https://goo.gl/yqv4Q4.

Parece que o push silencioso geral não será implementado no Chrome. Em vez disso, os autores da especificação estão explorando uma API de orçamento que permite que apps da Web enviem um determinado número de mensagens push silenciosas com base no uso do app.

Opção applicationServerKey

Este documento mencionava chaves de servidor de aplicativos. Um serviço de push usa chaves do servidor de aplicativos para identificar o aplicativo que está inscrevendo um usuário e garantir que o mesmo aplicativo envie mensagens para ele.

As chaves do servidor de aplicativos são um par de chaves pública e privada exclusivo do seu aplicativo. Mantenha a chave privada em segredo para seu aplicativo e compartilhe a chave pública livremente.

A opção applicationServerKey transmitida para a chamada subscribe() é a chave pública do seu aplicativo. O navegador transmite essa chave para um serviço de push ao inscrever o usuário, o que permite que o serviço vincule a chave pública do aplicativo ao PushSubscription do usuário.

No diagrama a seguir estão ilustradas essas etapas.

  1. Carregue seu web app em um navegador e chame subscribe(), transmitindo sua chave pública do servidor de aplicativos.
  2. Em seguida, o navegador faz uma solicitação de rede para um serviço de push, que gera um endpoint, associa esse endpoint à chave pública do aplicativo e o retorna ao navegador.
  3. O navegador adiciona esse endpoint ao PushSubscription, que é retornado pela promessa subscribe().

Diagrama que ilustra como a chave pública do servidor de aplicativos é usada no método `subscribe()`.

Ao enviar uma mensagem push, crie um cabeçalho Authorization que contenha informações assinadas com a chave privada do servidor de aplicativos. Quando o serviço de push recebe uma solicitação para enviar uma mensagem push, ele valida esse cabeçalho Authorization assinado pesquisando a chave pública vinculada ao endpoint que recebe a solicitação. Se a assinatura for válida, o serviço de push saberá que a solicitação veio do servidor de aplicativos com a chave privada correspondente. Essa é uma medida de segurança que impede que outras pessoas enviem mensagens aos usuários do seu aplicativo.

Diagrama que ilustra como a chave privada do servidor de aplicativos é usada ao enviar uma mensagem.

Tecnicamente, o applicationServerKey é opcional. No entanto, a implementação mais simples no Chrome exige isso, e outros navegadores podem exigir no futuro. Ele é opcional no Firefox.

A especificação VAPID define a chave do servidor de aplicativos. Quando você encontrar referências a chaves de servidor de aplicativos ou chaves VAPID, lembre-se de que elas são iguais.

Criar chaves de servidor de aplicativos

Para criar um conjunto público e privado de chaves do servidor de aplicativos, acesse web-push-codelab.glitch.me ou use a linha de comando web-push para gerar chaves da seguinte maneira:

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

Crie essas chaves apenas uma vez para seu aplicativo e mantenha a chave privada em sigilo.

Permissões e subscribe()

Chamar subscribe() tem um efeito colateral. Se o web app não tiver permissão para mostrar notificações quando você chamar subscribe(), o navegador vai solicitar as permissões para você. Isso é útil se a interface do usuário funcionar com esse fluxo, mas, se você quiser mais controle (o que a maioria dos desenvolvedores quer), use a API Notification.requestPermission(), que foi discutida anteriormente neste documento.

Visão geral do PushSubscription

Você chama subscribe(), passa opções e recebe uma promessa que é resolvida como um PushSubscription. Exemplo:

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 mensagens push a esse usuário. Se você imprimir o conteúdo usando JSON.stringify(), vai aparecer o seguinte:

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

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

O objeto keys contém os valores usados para criptografar os dados da mensagem enviados com uma mensagem push. Neste documento, vamos falar sobre a criptografia de mensagens mais adiante.

Enviar uma assinatura para seu servidor

Depois de ter uma assinatura por push, envie-a para o servidor. Você determina como enviar, mas uma dica é usar JSON.stringify() para extrair todos os dados necessários do objeto de assinatura. Como alternativa, você pode montar manualmente o mesmo resultado. Por exemplo:

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 a assinatura da página da Web, use o seguinte:

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 Node.js 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 do PushSubscription no seu servidor, você pode enviar uma mensagem ao usuário a qualquer momento.

Renove a assinatura regularmente 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. Em contraste, um DOMHighResTimeStamp indica o horário exato de expiração. Na prática, porém, os navegadores geralmente deixam as assinaturas expirarem. Por exemplo, isso pode acontecer se nenhuma notificação push for recebida por muito tempo ou se o navegador detectar que o usuário não está usando um app com permissão de notificação push. Um padrão para evitar isso é reincrever o usuário a cada notificação recebida, como mostra o snippet a seguir. Para isso, você precisa enviar notificações com frequência suficiente para evitar que o navegador expire automaticamente a inscrição. Pondere com cuidado as vantagens e desvantagens das necessidades legítimas de notificação em relação ao envio involuntário de spam ao usuário apenas para evitar o vencimento da assinatura. Em resumo, não tente burlar os esforços do navegador para proteger o usuário de assinaturas de notificações esquecidas há muito tempo.

/* 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

Confira algumas perguntas comuns:

É possível mudar o serviço de push usado por um navegador?

Não, o navegador seleciona o serviço de push. Como discutido neste documento com a chamada subscribe(), o navegador faz solicitações de rede ao serviço de push para recuperar os detalhes que compõem o PushSubscription.

Serviços de push diferentes usam APIs diferentes?

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

Essa API comum, chamada de Web Push Protocol (link em inglês), descreve a solicitação de rede que seu aplicativo faz para acionar uma mensagem push.

Se você inscrever um usuário no computador, ele também vai estar inscrito no smartphone?

Não. Um usuário precisa se registrar para receber mensagens push em cada navegador em que quer receber mensagens. Isso também exige que o usuário conceda permissão em cada dispositivo.

Próximas etapas

Codelabs