Verificación de identidad
Tu write key pública identifica tu proyecto, pero no autentica a nadie: cualquiera puede abrir una consola del navegador y enviar eventos como distinct_id: "[email protected]". La verificación de identidad cierra ese agujero — tu backend firma un JWT de vida corta que prueba que este navegador realmente es este usuario, y Kilden lo verifica en cada evento identificado.
El tráfico anónimo no se ve afectado: no hay identidad que falsificar, así que los eventos anónimos nunca necesitan token.
Cómo funciona
Sección titulada «Cómo funciona»- En la configuración del proyecto creas un identity secret (separado de tus write keys). Cada secret tiene un
kid(key id), y varios pueden estar activos a la vez, así que la rotación no tiene ventana de corte. - En el login, tu backend firma un JWT con ese secret — HS256, con el id del usuario en
sub. - El SDK envía el token con cada batch de eventos como
Authorization: Bearer <token>. - El pipeline lo verifica: firma contra un
kidactivo,expválido, ysubigual aldistinct_iddel evento. El resultado se guarda en cada evento comoverified.
El token
Sección titulada «El token»Payload de claims:
{ "sub": "user_4821", "iat": 1752192000, "exp": 1752195600, "traits": { "plan": "pro" }}sub— obligatorio. Debe ser igual aldistinct_idque usará el navegador (lo que le pasas aidentify()).exp— obligatorio. Mantenlo corto (minutos a horas); el SDK lo refresca automáticamente.kid— obligatorio, en el header del JWT (no en el payload). Identifica qué identity secret del proyecto firmó el token.traits— opcional. Traits firmados, aplicados con verificación del servidor.- Algoritmo: solo HS256. Los tokens firmados con cualquier otra cosa (incluido
none) fallan la verificación.
Firmar en tu backend
Sección titulada «Firmar en tu backend»import jwt from 'jsonwebtoken';
// Desde la configuración del proyecto → Verificación de identidad.// El secret nunca sale de tu backend.const KILDEN_IDENTITY_SECRET = process.env.KILDEN_IDENTITY_SECRET;const KILDEN_IDENTITY_KID = process.env.KILDEN_IDENTITY_KID;
app.get('/api/kilden-token', requireAuth, (req, res) => { const token = jwt.sign( { sub: req.user.id, // debe coincidir con el id que pasas a kilden.identify() traits: { plan: req.user.plan }, }, KILDEN_IDENTITY_SECRET, { algorithm: 'HS256', expiresIn: '1h', keyid: KILDEN_IDENTITY_KID }, ); res.json({ token });});<?php
use Firebase\JWT\JWT;
// Desde la configuración del proyecto → Verificación de identidad.$secret = getenv('KILDEN_IDENTITY_SECRET');$kid = getenv('KILDEN_IDENTITY_KID');
function kildenIdentityToken(string $userId, array $traits = []): string{ $now = time();
$payload = [ 'sub' => $userId, // debe coincidir con el id pasado a kilden.identify() 'iat' => $now, 'exp' => $now + 3600, 'traits' => (object) $traits, ];
return JWT::encode($payload, $GLOBALS['secret'], 'HS256', $GLOBALS['kid']);}(Usa firebase/php-jwt; el cuarto argumento define el header kid.)
Conectar el SDK
Sección titulada «Conectar el SDK»Deja que el SDK gestione el ciclo de vida del token — lo refresca alrededor de un minuto antes de exp y reintenta una vez ante un 401:
kilden.init('YOUR_WRITE_KEY', { getIdentityToken: async () => { const res = await fetch('/api/kilden-token'); if (!res.ok) return null; return (await res.json()).token; },});
// después del login:kilden.identify('user_4821');Alternativas: pasar un token que ya tienes en el init (identityToken: '...'), por llamada (identify(id, traits, { token })), o de forma imperativa (setIdentityToken(token)). En el logout, reset() lo limpia.
El token viaja por batch, en el header Authorization: Bearer. Una excepción documentada: el flush de fin de página puede caer a sendBeacon, que no puede llevar headers, así que el token va en el body como identity_token — misma protección TLS, se chequea en segundo lugar.
Modos de enforcement
Sección titulada «Modos de enforcement»La verificación se computa siempre — cada evento identificado recibe verified: true/false sin importar el modo. El modo solo gobierna qué pasa con las mutaciones de identidad no verificadas ($identify, $set, $set_once):
| Modo | Mutaciones de identidad no verificadas de usuarios identificados |
|---|---|
off |
Se aplican normalmente |
monitor |
Se aplican normalmente, pero cada una se cuenta para que veas exactamente qué bloquearía enforce |
enforce |
No se aplican: no pueden crear personas, enlazar identidades ni escribir traits. La resolución de identidad para eventos identificados no verificados pasa a ser solo lectura — los mapeos existentes siguen atribuyendo, pero no se crea nada nuevo |
Como monitor mide con precisión lo que enforce bloquearía, el rollout seguro es: instrumentar el token → observar cómo el contador de no verificados baja a cero → cambiar a enforce. El cambio no altera ningún dato, solo el comportamiento.
Dos reglas se cumplen en todos los modos:
- Los eventos nunca se descartan. Una verificación fallida conserva el evento con
verified: false; se excluye de los consumidores sensibles a la confianza (triggers de campañas, y lecturas del messenger cuando llegue la mensajería sobre canales identificados) pero permanece en tu analytics. - Los eventos anónimos siempre pasan. Su
verified: falsees una convención (no hay nada que verificar), no desconfianza. Los eventos server-side enviados con una secret key sonverified: true— la secret key es la autenticación.
Traits firmados
Sección titulada «Traits firmados»Los traits dentro del JWT se aplican como un $set verificado por el servidor y tienen prioridad sobre los traits sin firmar en el mismo evento. Úsalos para valores que el navegador no debería poder reclamar sobre sí mismo (plan, rol, estado de la cuenta). Para hechos que mueven dinero o mensajería, es preferible enviar el evento en sí server-side.
Rotación
Sección titulada «Rotación»Crea un secret nuevo (nuevo kid) en la configuración del proyecto, despliega tu backend firmando con él, y luego desactiva el kid viejo. Tener varios secrets activos permite rotar sin downtime; los tokens firmados por un kid desactivado fallan la verificación de inmediato.