Pular para o conteúdo

Webhooks

Receba um POST HTTP assinado quando pessoas entram, contas mudam ou um administrador altera um realm, e confirme que ele veio do Authio.

Um webhook é um POST que o Authio envia para um endereço seu quando algo acontece, para que os seus sistemas reajam sem consultar a API: criar o usuário no seu banco quando a conta nasce, abrir um chamado quando um refresh token é reutilizado, espelhar cada mudança administrativa num repositório de compliance.

Toda entrega leva o mesmo envelope JSON e uma assinatura HMAC-SHA256 feita com um segredo que só você e o Authio conhecem. Entregas que falham são reenviadas no ritmo que você escolher, e cada tentativa fica num histórico de entregas que você lê, filtra e reenvia pelo console.

Endpoints de client e de realm

Um endpoint é cadastrado em um de dois lugares, e isso decide o que ele pode ouvir:

Cadastrado emO que ouveOnde fica no console
Um clientO que acontece com as pessoas que usam aquela aplicação: logins, contas, tokens, chaves e suspensões.A aba Atividade do client, ou Webhooks com o client escolhido
Um realmToda mudança que um administrador faz em qualquer lugar do realm: admin_resource_created, admin_resource_updated e admin_resource_deleted.A aba Atividade do realm, em Trilha administrativa

Os dois conjuntos não se misturam: um endpoint de client não pode assinar os eventos administrativos, e um endpoint de realm só pode assinar esses. Um endpoint que nomeia um evento que nunca receberia é recusado no cadastro, em vez de ficar mudo.

Cadastre um endpoint

  1. Abra os webhooks do client

    Em Clients, abra o client e vá para a aba Atividade, ou abra Webhooks e escolha o client. Cadastrar um endpoint exige a permissão client:write.

  2. Descreva o endpoint

    Escolha Adicionar endpoint e preencha um nome (até 128 caracteres), a URL (um endereço https:// absoluto com host, até 2.048 caracteres), uma descrição opcional e pelo menos um evento. Retentativas e Primeira retentativa após definem como as entregas que falham são reenviadas; os valores padrão servem para a maioria dos receptores.

  3. Guarde o segredo de assinatura

    O Authio gera o segredo de assinatura do endpoint, um valor que começa com whsec_. Coloque-o na configuração do seu receptor, por exemplo como AUTHIO_WEBHOOK_SECRET.

    O segredo aparece uma única vez, logo depois que o endpoint é criado, e só para quem tem a permissão secret:read. Depois disso o console mostra apenas os quatro últimos caracteres; se perder o segredo, escolha Rotacionar secret.
  4. Envie um teste

    Escolha Enviar teste. O Authio envia um evento ping ao endpoint e registra a tentativa no histórico, com o código de status e o corpo que o seu receptor respondeu.

Tipos de evento

EventoEndpointEnviado quando
user_createdClientUm usuário é criado por qualquer caminho: cadastro, um administrador, uma importação ou o primeiro login por um provedor de identidade externo.
user_updatedClientO perfil ou o status de um usuário muda.
user_deletedClientUm administrador exclui um usuário. data traz a conta como ela estava no momento da exclusão.
login_succeededClientUma autenticação dá certo, em qualquer grant.
login_failedClientUma autenticação é recusada.
user_locked_outClientUma conta é bloqueada depois de tentativas que falharam seguidas.
token_revokedClientUm token é revogado explicitamente.
keys_rotatedClientAs chaves de assinatura e de criptografia do client são giradas, por um administrador ou pela rotação agendada.
token_reuse_detectedClientUm refresh token é apresentado de novo depois de já ter sido trocado. A sessão por trás dele foi encerrada.
user_suspendedClientUma suspensão é registrada, comece ela agora ou depois.
user_suspension_endedClientUma suspensão é levantada ou chega ao fim.
admin_resource_createdRealmUm administrador cria um recurso: um client, um papel, uma chave, qualquer coisa.
admin_resource_updatedRealmUm administrador altera um recurso.
admin_resource_deletedRealmUm administrador exclui um recurso.
pingAmbosVocê escolhe Enviar teste. Não pode ser assinado.

Só mudanças administrativas que deram certo são anunciadas; tentativas recusadas ficam na trilha de auditoria. Trocas de token de contas conectadas são registradas na trilha de acessos, mas não disparam login_succeeded nem login_failed, porque não são logins.

Payload

Todo evento chega no mesmo envelope. id identifica o evento e é o mesmo em todas as retentativas e reenvios dele; created_at vem em segundos Unix; realm_id e client_id dizem onde ele aconteceu; data depende do evento.

login_succeededJSON
{
  "id": "6c1e0f7a-3b9d-4f2e-9a51-0d8c2b7e4f13",
  "type": "login_succeeded",
  "created_at": 1791360000,
  "realm_id": "acme",
  "client_id": "acme-web",
  "data": {
    "grant_type": "AuthorizationCode",
    "user_id": "0f6b2c4e-8a1d-4c3b-9e7f-5a2d1c8b6e40",
    "username": "ana",
    "email": "ana@acme.com",
    "ip_address": "203.0.113.24",
    "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
    "session_id": "b7d4e2a1-5c3f-4e8b-a6d9-1f0c7e3b2a58",
    "failure_reason": null,
    "resource": null,
    "scopes": "openid profile email",
    "authentication_methods": ["pwd", "otp", "mfa"],
    "acr": "2",
    "identity_provider": null,
    "consumer_id": null,
    "consumer_name": null
  }
}

O que cada evento leva em data

EventosCampos em data
user_created, user_updated, user_deletedid, username, email, first_name, last_name, email_confirmed, status, realm_id, client_id
login_succeeded, login_failedgrant_type, user_id, username, email, ip_address, user_agent, session_id, failure_reason, resource, scopes, authentication_methods, acr, identity_provider, consumer_id, consumer_name
user_locked_outuser_id, username, email, lockout_end, access_failed_count
token_revokedtoken_id, subject, session_id, token_type_hint
token_reuse_detectedtoken_id, subject, session_id, ip_address, user_agent
keys_rotatedsigning_key_id, encryption_key_id, mais revoked_keys_count numa rotação manual, ou rotation_days e automatic numa rotação agendada
user_suspended, user_suspension_endedid, user_id, level, status, starts_at, ends_at, restricted_scopes, revokes_every_role, revoked_role_ids, sessions_terminated_at, lifted_at
admin_resource_created, admin_resource_updated, admin_resource_deletedaction, resource_type, resource_id, parent_id, realm_id, client_id, actor_type, actor_user_id, actor_username, actor_email, actor_credential_id, actor_credential_name, ip_address, method, path, mais details numa rotação de client secret
pingmessage, subscription

resource separa um login na sua aplicação de um token emitido para um servidor de recurso, como um servidor MCP, e authentication_methods, acr e identity_provider dizem como a pessoa entrou. Nos eventos administrativos o tipo de recurso vai em resource_type, para que os três nomes de evento não mudem quando a API cresce.

  • resource_id é o que foi criado, alterado ou excluído, e parent_id aquilo a que ele pertence, quando o endpoint o nomeia (a pessoa de uma suspensão, o provedor de identidade de um mapeamento de papel); os dois vêm null quando não há o que nomear, como numa importação que mexe em vários registros de uma vez.
  • actor_type diz quem fez a mudança: User, uma pessoa logada, ou Credential, uma credencial da conta (chave de API, token de acesso pessoal ou segredo de cliente). Para uma credencial, actor_credential_id e actor_credential_name a nomeiam, actor_user_id e actor_username nomeiam a pessoa em nome de quem ela age, e actor_email vem null.
  • A rotação do segredo de um client (admin_resource_updated) também traz details: mode — Immediate (o segredo anterior foi recusado na hora), Overlap (ele ainda funciona por um tempo) ou Compromise (ele foi recusado e os tokens emitidos com ele, revogados) — com previous_value_valid_until e issued_tokens_revoked_before, em UTC, nos modos a que pertencem. Veja Rotação de segredos.
  • Chamadas que não mudam nada não publicam nada: o teste enviado a um webhook, a reentrega de uma entrega, a prévia de projeção de token ou de mapeador de claims, o reenvio de um e-mail de confirmação e todo ensaio (dryRun) de uma importação. Uma mudança recusada, uma personificação e uma mudança fora de um realm também não publicam nada.

Cabeçalhos

CabeçalhoValor
X-Authio-Signaturet={timestamp},v1={signature}: o horário Unix em que a tentativa foi enviada e o HMAC-SHA256 de {timestamp}.{body} em hexadecimal minúsculo.
X-Authio-EventO nome do evento, ou ping, para você rotear sem ler o corpo.
X-Authio-Event-IdO id do evento, o mesmo em toda retentativa e todo reenvio.
Content-Typeapplication/json

Valide as assinaturas

Qualquer um que conheça o endereço do seu endpoint consegue enviar algo para ele, então confira cada entrega antes de agir:

  1. Leia o corpo bruto da requisição, byte a byte, antes de qualquer parse de JSON.
  2. Separe o X-Authio-Signature pelas vírgulas e pegue os valores de t e v1.
  3. Calcule o HMAC-SHA256 de {t}.{corpo bruto} usando como chave o segredo inteiro, com o prefixo whsec_, e codifique em hexadecimal minúsculo.
  4. Compare o resultado com v1 em tempo constante.
  5. Recuse a entrega se t estiver a mais de cinco minutos do seu relógio. O horário é definido no envio de cada tentativa, então retentativas e reenvios chegam com um horário novo.
lib/authio-webhook.tsTypeScript
import { createHmac, timingSafeEqual } from 'node:crypto'

const TOLERANCE_SECONDS = 5 * 60

export function verifyAuthioSignature(
  payload: string,
  header: string | null,
  secret: string,
): boolean {
  if (!header) return false

  let timestamp = ''
  let signature = ''

  for (const part of header.split(',')) {
    const [name, value = ''] = part.trim().split('=', 2)
    if (name === 't') timestamp = value
    if (name === 'v1') signature = value
  }

  if (!/^\d+$/.test(timestamp) || !/^[0-9a-f]{64}$/.test(signature)) {
    return false
  }

  const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp))
  if (age > TOLERANCE_SECONDS) return false

  const expected = createHmac('sha256', secret)
    .update(`${timestamp}.${payload}`)
    .digest()

  return timingSafeEqual(expected, Buffer.from(signature, 'hex'))
}

Depois, no seu handler, leia o corpo como texto, valide e responda rápido:

app/api/webhooks/authio/route.tsTypeScript
import { verifyAuthioSignature } from '@/lib/authio-webhook'

interface AuthioEvent {
  id: string
  type: string
  created_at: number
  realm_id: string | null
  client_id: string | null
  data: Record<string, unknown>
}

export async function POST(request: Request) {
  const payload = await request.text()
  const valid = verifyAuthioSignature(
    payload,
    request.headers.get('x-authio-signature'),
    process.env.AUTHIO_WEBHOOK_SECRET!,
  )

  if (!valid) {
    return new Response('Invalid signature', { status: 400 })
  }

  const event = JSON.parse(payload) as AuthioEvent

  console.log(`Received ${event.type} (${event.id})`)

  return new Response(null, { status: 204 })
}
Para conferir a configuração sem escrever código, abra Webhooks e escolha Verificar assinatura: cole o corpo bruto de uma entrega, o segredo e o cabeçalho X-Authio-Signature, e o console recalcula a assinatura no seu navegador. Nada do que você cola ali é enviado a lugar nenhum.

Retentativas e histórico de entregas

  • Uma tentativa dá certo quando o seu endpoint responde com qualquer status 2xx em até 10 segundos. Qualquer outro status, um tempo esgotado ou um erro de conexão conta como falha. Redirecionamentos não são seguidos.
  • Um evento que falhou é reenviado conforme a política do endpoint. Com os padrões (5 retentativas, a primeira depois de 60 segundos), elas chegam cerca de 1 minuto, 5 minutos, 25 minutos, 2 horas e 6 horas depois de cada falha: cada uma espera cinco vezes mais que a anterior, nunca mais de seis horas, com variação de até 20% para mais ou para menos.
  • Retentativas aceita de 0 a 10 (0 desliga) e Primeira retentativa após, de 30 a 3.600 segundos. Testes e reenvios manuais nunca são repetidos automaticamente.
  • Depois de 10 falhas seguidas o endpoint é desativado automaticamente; num endpoint de client, o dono da conta também recebe uma notificação crítica. Um sucesso zera a contagem, e as retentativas automáticas não somam nela. Retentativas já agendadas continuam saindo com o endpoint pausado ou desativado.

Um endpoint fica Active, Paused ou Disabled. Para trazer de volta um endpoint de realm desativado, corrija o receptor e ligue de novo Recebendo no editor dele. O console cadastra, testa, rotaciona o segredo e exclui endpoints de client; para alterar um deles, ou voltar um desativado para Active, atualize-o pela Admin API com PUT /api/v1/admin/realms/{realm}/clients/{client}/webhooks/{webhookId}.

O histórico dos endpoints de um client fica em Webhooks e na aba Atividade do client. Visão geral mostra tentativas, taxa de aceitação, latência e as respostas de falha mais comuns; Eventos lista cada ocorrência e até onde chegou o envio a todos os endpoints (Pending, Delivered, Partial ou Failed); Entregas lista cada tentativa com o corpo enviado e a sua resposta. Cada tentativa traz o seu tipo:

TipoO que é
EventA primeira entrega de um evento.
RetryUma retentativa automática de um evento que falhou.
ReplayUma entrega que alguém reenviou à mão.
TestUm ping enviado com Enviar teste.

Reenviar esta entrega manda de novo o payload guardado, sem alteração, mesmo que o usuário ou o recurso descrito tenha mudado desde então. Só funciona com o endpoint ativo e cancela as retentativas automáticas ainda agendadas para o mesmo evento. Nos endpoints de realm o console lista as entregas e envia testes; reenvio e métricas existem nos endpoints de client. O histórico de entregas é guardado por 30 dias.

Teste o seu endpoint

  • Enviar teste manda um ping assinado para um endpoint. Ele conta na saúde do endpoint, porque um endpoint que recusa um teste está com um problema de verdade.
  • Endpoint de captura, em Webhooks, abre um endereço temporário que registra cada requisição recebida — método, cabeçalhos e corpo — por 24 horas. Use-o como URL de um endpoint para ver exatamente o que o Authio envia antes de o seu receptor existir. O endereço é protegido só pelo caminho aleatório, então envie apenas dados de teste para ele.
  • O Authio só entrega em endereços públicos, conferidos depois da resolução de DNS: localhost e redes privadas são recusados. Para receber na sua máquina, use o endpoint de captura ou um túnel que dê a você um endereço HTTPS público.

Boas práticas

  • Valide antes de agir. Recuse toda entrega cuja assinatura não confere ou cujo horário está fora da sua tolerância.
  • Responda rápido, processe depois. Devolva um 2xx assim que a entrega for validada e guardada, e faça o trabalho demorado numa fila. Dez segundos é o limite, e um endpoint lento faz falhar entregas que ele de fato recebeu.
  • Torne os handlers idempotentes. A entrega é pelo menos uma vez: o mesmo evento pode chegar de novo por uma retentativa ou um reenvio. Guarde o id (também em X-Authio-Event-Id) do que já processou e ignore as repetições.
  • Não dependa da ordem. Uma retentativa pode chegar horas depois de um evento mais novo. Use created_at, ou leia o estado atual pela API, quando a ordem importar.
  • Trate `token_reuse_detected` como incidente. Ele indica que um refresh token muito provavelmente foi roubado: alguém além do titular o apresentou.
  • Planeje a rotação do segredo. Rotacionar secret invalida o segredo anterior na hora, sem período de sobreposição, então atualize o receptor em seguida. As entregas que falharem nesse intervalo são reenviadas conforme a política do endpoint, já assinadas com o segredo novo.

Webhooks de notificação

À parte dos webhooks de eventos, as notificações operacionais da sua conta — segredos vencendo, falhas de login fora do comum, um webhook desativado, cotas perto do limite — também podem ser enviadas para uma URL. O dono da conta define isso em Notificações › Preferências. O corpo traz id, type, severity, title, description, resource_id, realm_id e created_at. Esses envios não são assinados e não aparecem no histórico de entregas, então trate-os como alertas, não como fonte da verdade.

Próximos passos

Cookies de analytics

Gostaríamos de medir como o console é usado, com o Google Analytics, para enxergar em quais telas as pessoas travam. Isso grava um cookie que reconhece este navegador na sua próxima visita.

Recusar não muda mais nada. Os cookies que mantêm você conectado e lembram o seu idioma e o seu tema são necessários para o console funcionar, e ficam de qualquer forma. Ler a Política de Privacidade