Quando o Chrome começou a oferecer suporte à API Web Push, ele dependia do serviço de push do Firebase Cloud Messaging (FCM, na sigla em inglês), anteriormente conhecido como Google Cloud Messaging (GCM, na sigla em inglês). Isso é necessário usando uma API reservada. Isso permitiu que o Chrome disponibilizasse a API Web Push para os desenvolvedores em um momento em que a especificação do protocolo Web Push ainda estava sendo escrita e, mais tarde, forneceu autenticação (ou seja, o remetente da mensagem é quem diz ser) em um momento em que o protocolo Web Push não tinha isso. Boa notícia: nenhuma dessas afirmações é verdadeira mais.
O FCM / GCM e o Chrome agora oferecem suporte ao protocolo de push na Web padrão, e a autenticação do remetente pode ser feita com a implementação do VAPID. Isso significa que seu app da Web não precisa mais de um "gcm_sender_id".
Neste artigo, descreverei primeiro como converter seu código de servidor atual para usar o Web Push Protocol com o FCM. A seguir, vou mostrar como implementar o VAPID no código do cliente e do servidor.
O FCM oferece suporte ao protocolo de push da Web
Vamos começar com um pouco de contexto. Quando seu aplicativo da Web se registra para uma
assinatura push, ele recebe o URL de um serviço push. Seu servidor vai usar
esse endpoint para enviar dados ao usuário pelo seu app da Web. No Chrome, você vai receber
um endpoint do FCM se assinar um usuário sem VAPID. Vamos abordar o VAPID
mais adiante. Antes do FCM oferecer suporte ao protocolo de push da Web, era necessário extrair o ID de registro
do FCM do final do URL e colocá-lo no cabeçalho antes
de fazer uma solicitação da API do FCM. Por exemplo, um endpoint do FCM de
https://android.googleapis.com/gcm/send/ABCD1234
teria um ID de registro
de "ABCD1234".
Agora que o FCM oferece suporte ao protocolo Web Push, você pode deixar o endpoint intacto e usar o URL como um endpoint do protocolo Web Push. Isso alinha o navegador com o Firefox e, esperamos, com todos os outros navegadores futuros.
Antes de mergulhar no VAPID, precisamos garantir que o código do servidor processe corretamente o endpoint do FCM. Abaixo está um exemplo de como fazer uma solicitação para um serviço de push no Node. Observe que, para o FCM, estamos adicionando a chave de API aos cabeçalhos de solicitação. Para outros endpoints de serviço de push, isso não será necessário. Para o Chrome anterior à versão 52, Opera Android e o navegador Samsung, ainda é necessário incluir um "gcm_sender_id" no manifest.json do seu app da Web. A chave de API e o ID do remetente são usados para verificar se o servidor que faz as solicitações tem permissão para enviar mensagens ao usuário que recebe.
const headers = new Headers();
// 12-hour notification time to live.
headers.append('TTL', 12 * 60 * 60);
// Assuming no data is going to be sent
headers.append('Content-Length', 0);
// Assuming you're not using VAPID (read on), this
// proprietary header is needed
if(subscription.endpoint
.indexOf('https://android.googleapis.com/gcm/send/') === 0) {
headers.append('Authorization', 'GCM_API_KEY');
}
fetch(subscription.endpoint, {
method: 'POST',
headers: headers
})
.then(response => {
if (response.status !== 201) {
throw new Error('Unable to send push message');
}
});
Lembre-se de que essa é uma mudança na API do FCM / GCM. Portanto, não é necessário atualizar as assinaturas, basta mudar o código do servidor para definir os cabeçalhos, conforme mostrado acima.
Introdução ao VAPID para identificação do servidor
VAPID é o novo nome abreviado de
"Identificação voluntária do servidor de aplicativos". Essa
nova especificação define essencialmente um handshake entre o servidor do app e o serviço
push e permite que o serviço push confirme qual site está enviando mensagens.
Com o VAPID, é possível evitar as etapas específicas do FCM para enviar uma mensagem push. Você
não precisa mais de um projeto do Firebase, um gcm_sender_id
ou um
cabeçalho Authorization
.
O processo é bem simples:
- O servidor do aplicativo cria um par de chaves pública/privada. A chave pública é fornecida ao seu app da Web.
- Quando o usuário escolher receber pushs, adicione a chave pública ao objeto de opções da chamada subscribe().
- Quando o servidor do app enviar uma mensagem de push, inclua um JSON Web Token assinado junto com a chave pública.
Vamos analisar essas etapas em detalhes.
Criar um par de chaves pública/privada
Não sou muito bom em criptografia, então aqui está a seção relevante da especificação sobre o formato das chaves públicas/privadas VAPID:
Os servidores de aplicativos precisam gerar e manter um par de chaves de assinatura que possa ser usado com a assinatura digital de curva elíptica (ECDSA) na curva P-256.
Veja como fazer isso na biblioteca de nós de push web:
function generateVAPIDKeys() {
var curve = crypto.createECDH('prime256v1');
curve.generateKeys();
return {
publicKey: curve.getPublicKey(),
privateKey: curve.getPrivateKey(),
};
}
Como assinar com a chave pública
Para inscrever um usuário do Chrome para push com a chave pública VAPID, você precisa transmitir a chave pública como um Uint8Array usando o parâmetro applicationServerKey
do métodosubscribe().
const publicKey = new Uint8Array([0x4, 0x37, 0x77, 0xfe, …. ]);
serviceWorkerRegistration.pushManager.subscribe(
{
userVisibleOnly: true,
applicationServerKey: publicKey
}
);
Você vai saber se ele funcionou examinando o endpoint no objeto de assinatura
resultante. Se a origem for fcm.googleapis.com
, ele vai funcionar.
https://fcm.googleapis.com/fcm/send/ABCD1234
Como enviar uma mensagem push
Para enviar uma mensagem usando o VAPID, você precisa fazer uma solicitação normal do protocolo Web Push com dois cabeçalhos HTTP adicionais: um cabeçalho de autorização e um de chave criptográfica.
Cabeçalho de autorização
O cabeçalho Authorization
é um JSON Web Token (JWT) assinado com "WebPush" na frente.
Um JWT é uma forma de compartilhar um objeto JSON com um intermediário, de forma que a parte remetente possa assiná-lo e o destinatário possa verificar se a assinatura é do remetente esperado. A estrutura de um JWT é composta por três strings criptografadas, unidas por um único ponto entre elas.
<JWTHeader>.<Payload>.<Signature>
Cabeçalho JWT
O cabeçalho JWT contém o nome do algoritmo usado para a assinatura e o tipo de token. Para VAPID, isso precisa ser:
{
"typ": "JWT",
"alg": "ES256"
}
Em seguida, ele é codificado em base64 e forma a primeira parte do JWT.
Payload
O payload é outro objeto JSON que contém o seguinte:
- Público-alvo ("aud")
- Essa é a origem do serviço push (NÃO a origem do seu site).
Em JavaScript, faça o seguinte para conseguir o público-alvo:
const audience = new URL(subscription.endpoint).origin
- Essa é a origem do serviço push (NÃO a origem do seu site).
Em JavaScript, faça o seguinte para conseguir o público-alvo:
- Prazo de validade ("exp")
- É o número de segundos até que a solicitação seja considerada expirada. PRECISA estar dentro de 24 horas da solicitação, em UTC.
- Assunto ("sub")
- O assunto precisa ser um URL ou um URL
mailto:
. Isso fornece um ponto de contato caso o serviço de push precise entrar em contato com o remetente da mensagem.
- O assunto precisa ser um URL ou um URL
Um exemplo de payload pode ser parecido com este:
{
"aud": "http://push-service.example.com",
"exp": Math.floor((Date.now() / 1000) + (12 * 60 * 60)),
"sub": "mailto: my-email@some-url.com"
}
Esse objeto JSON tem URL codificado em base64 e forma a segunda parte do JWT.
Assinatura
A assinatura é o resultado da união do cabeçalho codificado e do payload com um ponto e, em seguida, criptografa o resultado usando a chave privada VAPID criada anteriormente. O resultado precisa ser anexado ao cabeçalho com um ponto.
Não vou mostrar um exemplo de código para isso, já que há várias bibliotecas que usam os objetos JSON de cabeçalho e payload e geram essa assinatura para você.
O JWT assinado é usado como o cabeçalho de autorização com "WebPush" no início e tem a seguinte aparência:
WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTQ2NjY2ODU5NCwic3ViIjoibWFpbHRvOnNpbXBsZS1wdXNoLWRlbW9AZ2F1bnRmYWNlLmNvLnVrIn0.Ec0VR8dtf5qb8Fb5Wk91br-evfho9sZT6jBRuQwxVMFyK5S8bhOjk8kuxvilLqTBmDXJM5l3uVrVOQirSsjq0A
Observe algumas coisas sobre isso. Primeiro, o cabeçalho de autorização literalmente contém a palavra "WebPush" e deve ser seguido por um espaço e pelo JWT. Observe também os pontos que separam o cabeçalho, o payload e a assinatura do JWT.
Cabeçalho Crypto-Key
Além do cabeçalho de autorização, adicione sua chave pública VAPID ao cabeçalho
Crypto-Key
como uma string codificada em base64 com p256ecdsa=
adicionada.
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo
Quando você envia uma notificação com dados criptografados, você já
está usando o cabeçalho Crypto-Key
. Para adicionar a chave do servidor do aplicativo,
basta adicionar um ponto e vírgula antes de adicionar o conteúdo acima, resultando
em:
dh=BGEw2wsHgLwzerjvnMTkbKrFRxdmwJ5S_k7zi7A1coR_sVjHmGrlvzYpAT1n4NPbioFlQkIrTNL8EH4V3ZZ4vJE;
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaN
Verdade dessas mudanças
Com o VAPID, não é mais necessário se inscrever em uma conta do GCM para usar push no Chrome. Você pode usar o mesmo caminho de código para inscrever um usuário e enviar uma mensagem para ele no Chrome e no Firefox. Ambos estão seguindo os padrões.
É importante lembrar que no Chrome 51 e versões anteriores, no Opera para
Android e no navegador Samsung, ainda será necessário definir o gcm_sender_id
no manifesto do app da Web e adicionar o cabeçalho de autorização ao
endpoint do FCM que será retornado.
O VAPID oferece uma saída para esses requisitos proprietários. Se você implementar o VAPID, ele funcionará em todos os navegadores compatíveis com push da Web. Como mais navegadores oferecem suporte a VAPID, você pode decidir quando descartar o gcm_sender_id
do seu manifesto.