For developers
Identity verification
identify() runs in the browser, so anyone can call it with someone else's user id and load that person's guide progress and attributes into their own browser. Identity verification closes that gap: your server signs each user id with a secret only it knows, and the User Onboarder server checks the signature.
How it works
- The workspace has a secret key (Settings → Keys, owners only).
- Your server computes
HMAC-SHA256(secretKey, userId)as lowercase hex and puts it in the page. - The browser passes it to
identify():
UserOnboarder.identify(user.id, { plan: user.plan }, { hash: "{{ user_hash }}" });
The id you sign must be exactly the string you pass to identify().
Compute the hash
Node.js
import { createHmac } from "crypto";
const hash = createHmac("sha256", process.env.USER_ONBOARDER_SECRET)
.update(String(user.id))
.digest("hex");
Python / Django
import hashlib, hmac
user_hash = hmac.new(
settings.USER_ONBOARDER_SECRET.encode(),
str(user.id).encode(),
hashlib.sha256,
).hexdigest()
PHP
$hash = hash_hmac('sha256', (string) $user->id, getenv('USER_ONBOARDER_SECRET'));
For a single-page app, return the hash from the endpoint that returns the current user (e.g. /api/me), next to the user's id.
Roll it out
- Ship the code that sends
hashwith everyidentify()call. Verification isn't enforced yet, so nothing breaks while you deploy, but a wrong hash is always rejected. Test before you ship. - Check that guides still load for signed-in users.
- In Settings, tick Require identity verification. From now on,
identify()without a valid hash is refused and that user sees no guides.
Anonymous visitors aren't affected.
Keep the secret safe
- Store it like a password: environment variable or secret manager, never in front-end code or a public repository.
- If it leaks, an owner can Rotate it in Settings. Users stop loading guides until your server uses the new secret, so update the server right after rotating.