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
- You generate a private key in the Produktly dashboard
- On your backend, you compute an HMAC-SHA256 hash of the user's ID using the private key
- You pass the hash to
identifyUseron the frontend - 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.
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.