Skip to content
Sendora
Create account
Security

Identity & anti-spoof

Anyone holding your public key could call identify("someone_else"). Strict identity mode forces every identify() call to carry an identityToken signed by your backend, so only you can attest that a user is who they claim to be.

When to turn it on

  • You run Sendora on an authenticated product (dashboards, apps, logged-in surfaces).
  • You're sending sensitive traits — plan, MRR, email — that you don't want a stranger overwriting.
  • You intend to build audiences or journeys keyed on user ID. A poisoned profile is a poisoned campaign.

Public, unauthenticated marketing pages are fine without it.

1. Rotate your identity secret

In Dashboard → Settings → Identity, click Rotate secret. You'll see a sis_… value exactly once. Store it in your backend's secret manager — it is never shown again.

SENDORA_IDENTITY_SECRET=sis_xxxxxxxx...

2. Sign a token in your backend

When a user loads your page, compute HMAC-SHA256(secret, userId), hex-encode it, and prefix with hmac_v1:. Ship it to the browser alongside the authenticated page response.

import { createHmac } from class="tk-s">"node:crypto";

export function signIdentity(userId: string) {
  const secret = process.env.SENDORA_IDENTITY_SECRET!;
  const mac = createHmac(class="tk-s">"sha256", secret).update(userId).digest(class="tk-s">"hex");
  return class="tk-s">`hmac_v1:${mac}`;
}

3. Pass the token to the SDK

Call identify() with the token as the third argument.

import { Sendora } from class="tk-s">"@sendoracloud/sdk-web";

const s = new Sendora({ publicKey: class="tk-s">"pk_live_..." });

s.identify(
  class="tk-s">"u_123",
  { email: class="tk-s">"sam@acme.co", plan: class="tk-s">"startup" },
  { identityToken: window.__SENDORA_IDENTITY_TOKEN__ }
);

4. Flip the strict toggle

Back in Settings → Identity, enable Strict identity. Every event that arrives without a valid token is now rejected:

  • Single-event POST /events401 with IDENTITY_UNVERIFIED.
  • Batch POST /events/batch — the failing events are dropped from the batch; valid ones still land. The response returns a rejected array with the index and reason for each skipped event.

How verification works

  • Only the hmac_v1: scheme is accepted today. Future schemes will bump the prefix.
  • The backend caches your secret in-memory for 60 seconds after first use. Rotating the secret propagates within that window.
  • Comparison is constant-time. Tokens are never logged.

Rotating without downtime

  1. Deploy backend that signs with the new secret.
  2. Rotate the secret in the dashboard.
  3. Within 60 s, the backend cache drops the old value and starts verifying the new one.

To avoid any gap, keep strict mode off for one deploy cycle, then re-enable it after the rotation settles.

Troubleshooting

  • Every event rejected. Confirm the signed string is the exact userId you pass to identify() — no trimming, no prefix.
  • Tokens accepted in dev, rejected in prod. You rotated the secret in the dashboard but the backend still signs with the old one. Update your env var.
  • Works for single events, fails for batch. Batch endpoints drop failing events silently — check the rejected array in the response.