Pular para o conteúdo

Verificação de identidade

Sua write key pública identifica seu projeto, mas não autentica ninguém: qualquer um pode abrir um console de navegador e enviar eventos como distinct_id: "[email protected]". A verificação de identidade fecha esse buraco — seu backend assina um JWT de vida curta provando que este navegador realmente é este usuário, e o Kilden o verifica em cada evento identificado.

O tráfego anônimo não é afetado: não há identidade a forjar, então eventos anônimos nunca precisam de token.

  1. Nas configurações do projeto você cria um identity secret (separado das suas write keys). Cada secret tem um kid (key id), e vários podem estar ativos ao mesmo tempo, então a rotação não tem janela de transição.
  2. No login, seu backend assina um JWT com esse secret — HS256, com o id do usuário em sub.
  3. O SDK envia o token com cada batch de eventos como Authorization: Bearer <token>.
  4. O pipeline o verifica: assinatura contra um kid ativo, exp válido e sub igual ao distinct_id do evento. O resultado é armazenado em cada evento como verified.

Payload de claims:

{
"sub": "user_4821",
"iat": 1752192000,
"exp": 1752195600,
"traits": { "plan": "pro" }
}
  • subobrigatório. Deve ser igual ao distinct_id que o navegador vai usar (o que você passa para identify()).
  • expobrigatório. Mantenha-o curto (minutos a horas); o SDK renova automaticamente.
  • kidobrigatório, no header do JWT (não no payload). Identifica qual identity secret do projeto assinou o token.
  • traits — opcional. Traits assinados, aplicados com verificação do servidor.
  • Algoritmo: somente HS256. Tokens assinados com qualquer outra coisa (incluindo none) falham na verificação.
import jwt from 'jsonwebtoken';
// Das configurações do projeto → Verificação de identidade.
// O secret nunca sai do seu 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, // deve bater com o id que você passa para 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;
// Das configurações do projeto → Verificação de identidade.
$secret = getenv('KILDEN_IDENTITY_SECRET');
$kid = getenv('KILDEN_IDENTITY_KID');
function kildenIdentityToken(string $userId, array $traits = []): string
{
$now = time();
$payload = [
'sub' => $userId, // deve bater com o id passado para kilden.identify()
'iat' => $now,
'exp' => $now + 3600,
'traits' => (object) $traits,
];
return JWT::encode($payload, $GLOBALS['secret'], 'HS256', $GLOBALS['kid']);
}

(Usa firebase/php-jwt; o quarto argumento define o header kid.)

Deixe o SDK gerenciar o ciclo de vida do token — ele renova cerca de um minuto antes do exp e tenta de novo uma vez ao receber 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;
},
});
// depois do login:
kilden.identify('user_4821');

Alternativas: passe um token que você já tem no init (identityToken: '...'), por chamada (identify(id, traits, { token })) ou de forma imperativa (setIdentityToken(token)). No logout, reset() o limpa.

O token viaja por batch, no header Authorization: Bearer. Uma exceção documentada: o flush de fim de página pode cair para sendBeacon, que não carrega headers, então o token vai no corpo como identity_token — mesma proteção TLS, verificado em segundo lugar.

A verificação é sempre computada — todo evento identificado recebe verified: true/false independentemente do modo. O modo governa apenas o que acontece com mutações de identidade não verificadas ($identify, $set, $set_once):

Modo Mutações de identidade não verificadas de usuários identificados
off Aplicadas normalmente
monitor Aplicadas normalmente, mas cada uma é contabilizada para você ver exatamente o que enforce bloquearia
enforce Não aplicadas: não podem criar pessoas, vincular identidades nem escrever traits. A resolução de identidade para eventos identificados não verificados vira somente leitura — mapeamentos existentes continuam atribuindo, mas nada novo é criado

Como monitor mede precisamente o que enforce bloquearia, o rollout seguro é: instrumente o token → observe o contador de não verificados cair a zero → mude para enforce. A mudança não altera dado nenhum, só o comportamento.

Duas regras valem em todos os modos:

  • Eventos nunca são descartados. Uma verificação que falha mantém o evento com verified: false; ele é excluído dos consumidores sensíveis a confiança (gatilhos de campanha, e leituras do messenger quando mensageria em canais identificados for lançada), mas permanece no seu analytics.
  • Eventos anônimos sempre passam. Seu verified: false é uma convenção (não há nada a verificar), não desconfiança. Eventos server-side enviados com uma secret key são verified: true — a secret key é a autenticação.

Traits dentro do JWT são aplicados como um $set verificado pelo servidor e têm prioridade sobre traits não assinados no mesmo evento. Use-os para valores que o navegador não deveria poder afirmar sobre si mesmo (plano, papel, status da conta). Para fatos que movimentam dinheiro ou mensageria, prefira enviar o próprio evento server-side.

Crie um novo secret (novo kid) nas configurações do projeto, faça o deploy do seu backend assinando com ele e então desative o kid antigo. Vários secrets ativos significam rotação sem downtime; tokens assinados por um kid desativado falham na verificação imediatamente.