A maioria dos textos sobre confiabilidade de webhooks trata um evento perdido como um problema de dados. Falta uma linha, você acaba percebendo, você preenche a lacuna (backfill). Chato, recuperável, quase invisível para quem está fora da engenharia.
Eventos de identidade quebram essa forma de ver, porque as duas direções não são simétricas.
Um evento access.granted que nunca chega produz um usuário que não consegue entrar, e usuários que não conseguem entrar avisam você em minutos. O problema se reporta sozinho. O sistema tem, de graça, uma camada de alerta humana embutida.
Um evento access.revoked que nunca chega produz um usuário que ainda consegue entrar. Ninguém reporta isso. A pessoa que se beneficia não tem motivo para mencionar, e todo o resto acredita que o offboarding foi concluído porque seu provedor de identidade diz que foi. A brecha continua até uma auditoria encontrá-la, o que pode levar um ano.
TL;DR
- Perder uma concessão de acesso é um chamado de suporte. Perder uma revogação é uma porta aberta sem alarme
- O painel do seu provedor de identidade mostra que a revogação deu certo, porque do lado dele deu
- O Clerk, e qualquer outro produto construído sobre o Svix, faz 8 tentativas ao longo de cerca de 27,5 horas e depois desativa o endpoint após 5 dias de falha total
- O endpoint desativado é, por si só, um estado silencioso, e é o que mais importa
- Trate a disponibilidade do receptor de webhooks como um controle de acesso, não como higiene de infraestrutura
A falha que o painel não consegue mostrar
Um admin remove alguém da organização. O provedor de identidade atualiza os próprios registros, invalida as próprias sessões e dispara o evento organizationMembership.deleted, user.deleted ou session.revoked para o seu endpoint.
Seu endpoint está no meio de um deploy e responde 502.
No console do provedor de identidade, a associação não existe mais. Na visão do admin, o offboarding está feito. Sua aplicação ainda tem uma linha dizendo que essa pessoa é membro daquela organização, e qualquer sessão em cache ou token de API que você emitiu a partir da concessão original continua válido.
Não há erro aparecendo em nenhum lugar que uma pessoa olhe. O log de webhooks do provedor tem uma entrega com falha, mas ninguém lê um log de webhooks a menos que já desconfie de algo.
As novas tentativas (retries) compram tempo, e depois param
O Clerk envia seus webhooks pelo Svix, e vários outros produtos de identidade e de infraestrutura fazem o mesmo. O cronograma é público e vale a pena conhecê-lo com precisão, porque ele define exatamente quanto tempo seu endpoint pode ficar quebrado antes de o Svix parar de tentar de novo uma revogação.
Oito tentativas: "Imediatamente, 5 segundos, 5 minutos, 30 minutos, 2 horas, 5 horas, 10 horas, 10 horas (cada uma somada à anterior)".
São cerca de 27,5 horas de cobertura, o que é realmente generoso, e está entre as mais longas de todos os remetentes da nossa referência de provedores de webhook. Uma janela de deploy, um incidente, uma sexta-feira ruim: tudo cabe com folga. Depois da oitava tentativa, o Svix envia um webhook operacional do tipo message.attempt.exhausted, que só é útil se você tiver um destino para os webhooks operacionais.
Depois vem o segundo limite: "Se todas as tentativas para um endpoint específico falharem por um período de 5 dias, o endpoint será desativado e um webhook operacional (EndpointDisabledEvent) será enviado para sua conta". A contagem tem uma condição: "A contagem só começa depois que várias entregas falharem em um intervalo de 24 horas, com pelo menos 12 horas de diferença entre a primeira e a última falha".
Leia o estado de desativado com atenção. Depois que um endpoint é desativado, novos eventos não entram na fila para ele. Eles não são entregues mais tarde, quando você conserta o endpoint. Toda revogação nessa janela fica sem entrega até alguém perceber e reenviá-la, e o único aviso é um webhook operacional enviado para uma conta em que ninguém configurou um destino.
A assimetria é um dado de projeto
Depois que você aceita que as duas direções têm custos diferentes, algumas decisões comuns mudam.
Falhe fechado nos eventos que removem acesso. Se seu handler não consegue processar o evento session.revoked, o comportamento correto não é responder 200 e registrar um aviso no log. Responda com um 5xx para que o cronograma de novas tentativas entre em ação, e garanta que seus próprios alertas disparem. Numa concessão, você pode se dar ao luxo do otimismo. Numa revogação, não.
Não dependa só do webhook. Eventos push são uma otimização de latência, não uma fonte da verdade. Uma conciliação periódica contra a API do provedor de identidade, perguntando "quem deveria ter acesso agora", transforma uma revogação perdida para sempre em uma que se corrige sozinha na próxima varredura. É o mesmo argumento do modelo híbrido de Webhooks vs polling, e identidade é o caso em que ele deixa de ser opcional. Rode a conciliação de hora em hora e o pior caso passa a ser uma hora de acesso desatualizado, e não para sempre.
Faça suas próprias sessões expirarem. Se sua aplicação emite a própria sessão ou o próprio token a partir de um evento de identidade, um TTL curto com uma renovação que confere de novo a associação faz uma revogação perdida expirar sozinha. Tokens de longa duração transformam um único webhook perdido em acesso por tempo indeterminado.
A ordem também importa aqui. Um evento member.added entregue numa nova tentativa depois de um member.removed vai adicionar de novo alguém que você acabou de remover, e nenhum evento foi processado duas vezes. O padrão de guarda por versão, que só aplica uma atualização quando o timestamp dela é mais recente que o da linha, está em Você recebeu o mesmo webhook duas vezes. Para controle de acesso, prefira rejeitar de cara uma transição de estado inválida a depender de comparação de timestamps.
Saiba como fica seu endpoint quando ele é desativado. Esse é o alerta mais valioso de toda a categoria. Se seu webhook de identidade fica em silêncio por mais tempo que o intervalo normal, há algo errado, e "silêncio" é exatamente o sintoma do estado desativado. Uma verificação de "nenhum evento de identidade em 6 horas durante o horário comercial" pegaria isso. Nada mais vai pegar.
O que fazer com a pergunta da auditoria
Se você lida com SOC 2, ISO 27001 ou qualquer questionário de segurança de cliente, existe ali uma pergunta sobre desprovisionamento no prazo. A resposta honesta, se seu caminho de revogação é um único webhook sem conciliação, é que o desprovisionamento acontece prontamente, exceto quando silenciosamente não acontece, e você não consegue distinguir uma coisa da outra depois do fato.
Isso é difícil de colocar no papel. E também tem conserto com duas peças: um log durável de cada evento de identidade que chegou e uma rotina de conciliação cujo resultado você pode mostrar. O log responde "o que nos disseram, e quando"; a varredura responde "e se agimos com base nisso". Escrevemos sobre por que uma linha mutável no banco de dados é uma resposta fraca para a primeira pergunta em Um log de webhooks com adulteração detectável.
Onde o AnyHook entra
O problema estrutural é que a disponibilidade do seu receptor passou a fazer parte do seu controle de acesso, e receptores construídos sobre a infraestrutura da aplicação herdam o uptime da aplicação.
Apontar o provedor de identidade para in.anyhook.net/you/identity separa as duas coisas. O AnyHook confirma o recebimento e grava o evento na edge (o Cloudflare Worker que atende essa URL) em cerca de um segundo, então um deploy, um cold start ou uma queda do banco de dados do seu lado não produz nenhuma entrega com falha. A partir daí:
- As novas tentativas contra seu próprio endpoint acontecem em 5 minutos, 30 minutos, 3 horas, 18 horas e depois uma vez por dia. No Scale (cerca de 6 dias), isso dura mais que as 27,5 horas do Svix; no Pro, a última tentativa acontece em cerca de 22 horas e, no Free, em 35 minutos, e depois disso você faz o replay (um novo envio de um evento armazenado, feito por você) a partir do evento guardado
- O AnyHook armazena cada revogação antes de tentar a entrega, com criptografia em repouso, então "será que recebemos isso?" tem uma resposta independente dos logs da sua aplicação
- O replay de um intervalo de tempo transforma uma janela de deploy quebrado numa nova entrega, e não numa auditoria manual de quem deveria ter perdido o acesso
- Os alertas com 1, 5 e 20 falhas consecutivas chegam muito antes do limite de desativação de qualquer provedor
Nada disso elimina a varredura de conciliação, e você ainda deve rodar uma. Um relay de webhooks (um serviço que recebe, guarda e encaminha seus webhooks) armazena cada evento que aceita; só a varredura pega o que nunca foi enviado.
Conclusão
As concessões se corrigem sozinhas porque a pessoa afetada reclama. As revogações não, porque a pessoa afetada não reclama. Essa única assimetria é o motivo pelo qual os webhooks de identidade merecem um padrão de confiabilidade mais rígido que o resto das suas integrações, mesmo com payloads que parecem iguais.
Falhe fechado nas remoções, faça suas sessões expirarem, rode uma varredura de conciliação e coloque um alerta para silêncios inesperados. A alternativa é um controle de segurança cujo modo de falha é o silêncio.