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.

Os SDKs de servidor trazem um IdentitySigner que produz este token em três linhas — sem biblioteca de JWT, sem adivinhar o conjunto de claims, testado byte a byte contra o verificador da plataforma:

import { IdentitySigner } from "@kilden-io/node";
const signer = new IdentitySigner(process.env.KILDEN_IDENTITY_SECRET!, { kid: "k1" });
app.post("/kilden/identity", (req, res) => {
const user = req.user; // your auth middleware — never an id from request input
res.json({
distinct_id: String(user.id),
token: signer.sign(String(user.id), { traits: { plan: user.plan } }),
});
});
$signer = new Kilden\IdentitySigner(getenv('KILDEN_IDENTITY_SECRET'), ['kid' => 'k1']);
$token = $signer->sign($user->id, [
'traits' => ['plan' => $user->plan],
]);

A mesma classe existe em Python, Ruby e Go. Dois atalhos pulam até isso: o kilden/laravel traz uma rota publicável POST /kilden/identity, e o plugin de WordPress expõe /wp-json/kilden/v1/identity — ambos atrás da sua auth existente. No Laravel, @kildenScript no seu layout renderiza o SDK web com o callback do token já conectado a POST /kilden/identity.

Prefere uma biblioteca de JWT que você já usa? O token é um JWT HS256 padrão: sub (deve ser igual ao id que você passa ao kilden.identify()), iat, exp (TTLs curtos — uma hora basta), traits opcional, e o header kid nomeando o secret. Qualquer implementação correta de JWT verifica bem; os SDKs apenas tornam o certo a coisa fácil.

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, apenas na web: 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.