Webhooks
Recibe notificaciones HTTP en tiempo real cuando ocurren eventos de autenticación en tu proyecto. Sincroniza usuarios con tu base de datos, envía correos de bienvenida o activa flujos de trabajo cuando los usuarios se registran, inician sesión o son bloqueados.
¿Qué son los Webhooks?
Un webhook es un callback HTTP que tu servidor registra para recibir notificaciones de eventos en tiempo real. En lugar de consultar Authon para verificar si un usuario fue eliminado o bloqueado, Authon envía proactivamente una solicitud POST a tu URL en el momento en que ocurre el evento.
Usuario eliminado en el panel de Authon
→ Authon envía POST /webhooks/authon
→ Tu servidor recibe el evento y elimina al usuario de tu base de datosCómo funcionan los Webhooks
Cuando ocurre un evento en tu proyecto de Authon, la API envía una solicitud POST a tu endpoint registrado con un payload JSON. Cada solicitud incluye un encabezado X-Authon-Signature con el prefijo v1= — una firma HMAC-SHA256 calculada sobre timestamp.body usando tu secreto de firma del webhook. Siempre verifica esta firma antes de procesar el evento.
Configurar un Webhook
- 1Navega a Panel → Webhooks → Agregar Webhook
- 2Ingresa la URL de tu endpoint — debe ser accesible públicamente mediante HTTPS
- 3Selecciona los eventos que deseas recibir
- 4Copia el secreto de firma — se muestra solo una vez, guárdalo de forma segura
- 5Configura AUTHON_WEBHOOK_SECRET en el entorno de tu servidor
Tipos de eventos
| Evento | Disparador |
|---|---|
| user.created | Un nuevo usuario completa el registro |
| user.updated | El perfil de un usuario es actualizado |
| user.deleted | Una cuenta de usuario es eliminada permanentemente |
| user.signin | Un usuario inicia sesión (correo u OAuth) |
| user.signout | Un usuario cierra sesión |
| user.banned | Un administrador bloquea a un usuario |
| user.unbanned | Se levanta el bloqueo de un usuario |
| session.created | Se crea una nueva sesión (inicio de sesión, actualización de token) |
| session.revoked | Se revoca una sesión (cierre de sesión, revocación por admin) |
Formato del payload
Cada payload de webhook es un objeto JSON con una estructura de nivel superior consistente. El campo data contiene detalles específicos del evento.
{
"event": "user.created",
"data": {
"user": {
"id": "usr_abc123",
"email": "user@example.com",
"displayName": "Jane Doe",
"emailVerified": false,
"isBanned": false,
"publicMetadata": null,
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:30:00.000Z"
}
},
"timestamp": "2026-01-15T10:30:00.000Z"
}Para eventossession.*, el campo datatambién incluye un objeto sessioncon id, ipAddress y userAgent.
Verificación de firma
Cada solicitud de webhook contiene un encabezado X-Authon-Signature con el formato v1=<hex_digest>. La firma se calcula como HMAC-SHA256 sobre timestamp.rawBody usando tu secreto de webhook. Compara el resultado con igualdad de tiempo constante.
| Encabezado | Formato | Descripción |
|---|---|---|
| X-Authon-Signature | v1={hmac_sha256} | Firma HMAC-SHA256 para verificar el origen de la solicitud |
| X-Authon-Timestamp | ISO 8601 | Marca de tiempo ISO 8601 de cuándo se envió el evento |
| X-Authon-Event | user.created | El tipo de evento (ej. user.created, session.revoked) |
json() de Express re-serializa el cuerpo, lo que puede alterar los espacios en blanco y romper la firma.Ejemplo con Node.js
import { createHmac, timingSafeEqual } from "crypto";
import express from "express";
const router = express.Router();
function verifySignature(
rawBody: Buffer,
signature: string,
secret: string,
timestamp: string,
): boolean {
const payload = `${timestamp}.${rawBody.toString()}`;
const expected = createHmac("sha256", secret)
.update(payload)
.digest("hex");
const actual = signature.replace("v1=", "");
const expectedBuf = Buffer.from(expected, "hex");
const actualBuf = Buffer.from(actual, "hex");
return (
expectedBuf.length === actualBuf.length &&
timingSafeEqual(expectedBuf, actualBuf)
);
}
router.post(
"/webhooks/authon",
express.raw({ type: "application/json" }),
(req, res) => {
const signature = req.headers["x-authon-signature"] as string;
const timestamp = req.headers["x-authon-timestamp"] as string; // ISO 8601
const eventType = req.headers["x-authon-event"] as string;
const webhookSecret = process.env.AUTHON_WEBHOOK_SECRET!;
if (!verifySignature(req.body, signature, webhookSecret, timestamp)) {
return res.status(401).json({ error: "Invalid signature" });
}
const { event, data } = JSON.parse(req.body.toString());
switch (eventType) {
case "user.created":
// Sync new user to your database
break;
case "user.deleted":
// Remove user from your database
break;
case "user.updated":
// Update user data
break;
case "user.banned":
// Revoke app-level access
break;
}
res.status(200).json({ received: true });
}
);
export default router;Usando el SDK
El SDK@authon/node proporciona un helper de verificación integrado que maneja la comparación de tiempo constante por ti:
import { AuthonBackend } from "@authon/node";
const authon = new AuthonBackend(process.env.AUTHON_SECRET_KEY!);
router.post(
"/webhooks/authon",
express.raw({ type: "application/json" }),
(req, res) => {
const signature = req.headers["x-authon-signature"] as string;
const timestamp = req.headers["x-authon-timestamp"] as string;
let event: Record<string, unknown>;
try {
event = authon.webhooks.verify(
req.body,
signature,
timestamp,
process.env.AUTHON_WEBHOOK_SECRET!,
);
} catch {
return res.status(401).json({ error: "Invalid signature" });
}
console.log("Verified event:", event.event);
res.json({ received: true });
}
);Política de reintentos
Si tu endpoint retorna un código de estado diferente a 2xx o no responde dentro de 10 segundos, Authon reintenta la entrega con retroceso exponencial. Máximo 3 intentos en total.
| Intento | Retraso | Tiempo acumulado |
|---|---|---|
| Inicial | — | 0s |
| 1er reintento | 1 second | ~1s |
| 2do reintento | 2 seconds | ~3s |