Skip to main content

Identity Verification

Identity verification is an optional security feature that ensures only your backend can identify users. When enabled, all identifyUser calls must include an HMAC-SHA256 hash signed with one of your private keys. This prevents bad actors from impersonating users or sending arbitrary user data.

How it works

  1. You generate a private key in the Produktly dashboard
  2. On your backend, you compute an HMAC-SHA256 hash of the user's ID using the private key
  3. You pass the hash to identifyUser on the frontend
  4. Produktly's server verifies the hash before accepting the identity

Without identity verification, anyone who finds your client token (which is public in your script tag) could call identifyUser with any user ID. With identity verification enabled, they would also need your private key (which only your server knows) to produce a valid hash.

Setup

1. Generate a private key

Go to Settings > Private Keys and click Generate new private key. Copy the key and store it securely on your backend (e.g. as an environment variable). Never expose it in frontend code.

2. Enable identity verification

On the same page, toggle Require identity verification to on.

caution

Make sure you have added the hash to your identifyUser calls before enabling this setting. Once enabled, any identifyUser call without a valid hash will be rejected.

3. Generate the hash on your backend

Compute the HMAC-SHA256 hash of the user's ID (as a string) using your private key, and send the hex-encoded result to your frontend.

Node.js

const crypto = require("crypto");

const hash = crypto
.createHmac("sha256", process.env.PRODUKTLY_PRIVATE_KEY)
.update(String(userId))
.digest("hex");

// Send `hash` to your frontend (e.g. in your API response or rendered page)

Python

import hmac
import hashlib

hash = hmac.new(
os.environ["PRODUKTLY_PRIVATE_KEY"].encode(),
str(user_id).encode(),
hashlib.sha256
).hexdigest()

# Send `hash` to your frontend (e.g. in your API response or rendered template)

Ruby

hash = OpenSSL::HMAC.hexdigest(
"sha256",
ENV["PRODUKTLY_PRIVATE_KEY"],
user_id.to_s
)

# Send `hash` to your frontend

PHP

$hash = hash_hmac(
"sha256",
strval($userId),
getenv("PRODUKTLY_PRIVATE_KEY")
);

// Send $hash to your frontend

Go

mac := hmac.New(sha256.New, []byte(os.Getenv("PRODUKTLY_PRIVATE_KEY")))
mac.Write([]byte(fmt.Sprintf("%v", userId)))
hash := hex.EncodeToString(mac.Sum(nil))

// Send hash to your frontend

4. Pass the hash to identifyUser

Once your frontend receives the hash from your backend, pass it as the third argument:

window.Produktly.identifyUser(userId, metadata, {
hash: hashFromYourBackend,
});

Full example (Node.js + Express)

Backend:

const crypto = require("crypto");

app.get("/api/current-user", (req, res) => {
const user = req.user;

const hash = crypto
.createHmac("sha256", process.env.PRODUKTLY_PRIVATE_KEY)
.update(String(user.id))
.digest("hex");

res.json({
id: user.id,
name: user.name,
plan: user.plan,
produktlyHash: hash,
});
});

Frontend:

const res = await fetch("/api/current-user");
const user = await res.json();

window.Produktly.identifyUser(user.id, { name: user.name, plan: user.plan }, {
hash: user.produktlyHash,
});

FAQ

What happens if I enable identity verification without adding the hash?

All identifyUser calls will be rejected with a 403 error. Widgets that rely on user identification (e.g. checklists, changelogs with read tracking) will not work correctly for identified users. Anonymous users are not affected.

Can I use multiple private keys?

Yes. Produktly will check the hash against all active (non-archived) private keys for your account. This is useful for key rotation — generate a new key, update your backend, then delete the old key.

Does this affect anonymous users?

No. Identity verification only applies to identifyUser calls. If you don't call identifyUser, widgets will still work normally for anonymous users.