Identity Verification
Identity verification proves that SDK identify calls came from your
application. Your server signs identity data with a shared secret, and Aimdoc
verifies the signature before trusting it.
Identity verification applies to App Mode on the Activate plan — the
in-app identify path for signed-in users in your product. It does not
apply to anonymous website visitors or website-agent enrichment. For website
enrichment, see Visitor Identification.
How it works
- Your backend computes an HMAC-SHA256 signature using your Aimdoc identity verification secret.
- Your frontend passes that signature as
user_hashtoaimdoc.identify. - Aimdoc normalizes the submitted identity fields, recomputes the signature, and rejects the request if any canonical signed value was changed.
Compute signatures on your server. Never include the identity-verification secret in browser code, environment variables exposed to the browser, or SDK payloads.
User-only identifies use the legacy signature over the user's external ID and
normalized email. Identifies containing an account object use the v2
canonical payload, which also covers the account external ID, name, and
domain.
Configure a signing secret
In the Aimdoc dashboard:
- Open Integrations.
- Select Identity Verification.
- Choose Generate signing secret.
- Store the secret in your backend's secret manager or server environment.
Treat this value like an API credential. Anyone with the secret can generate valid identity assertions for your organization.
Sign identify calls
For a user-only identify, sign
"{external_id}:{normalized_email}":
// Server (Node.js)
import { createHmac } from 'crypto'
const userHash = createHmac(
'sha256',
process.env.AIMDOC_IDENTITY_SECRET,
)
.update(`${user.id}:${user.email.trim().toLowerCase()}`)
.digest('hex')
Pass the server-generated value to the browser:
aimdoc.identify({
external_id: user.id,
email: user.email,
user_hash: userHashFromServer,
})
Account identity affects shared account records, so every identify containing
account requires a v2 signature—even before organization-wide enforcement is
enabled. The v2 hash must cover the complete canonical account payload.
// Server (Node.js)
import { createHmac } from 'crypto'
const canonicalizeDomain = (value = '') => {
const rawValue = value.trim()
if (!rawValue) return ''
const url = new URL(
rawValue.includes('://') ? rawValue : `https://${rawValue}`,
)
return url.hostname
.toLowerCase()
.replace(/\.+$/, '')
.replace(/^www\./, '')
}
const signedPayload = JSON.stringify({
account: {
domain: canonicalizeDomain(account.domain),
external_id: account.external_id.trim(),
name: (account.name || '').trim(),
},
email: user.email.trim().toLowerCase(),
external_id: user.id.trim(),
version: 2,
})
const userHash = createHmac(
'sha256',
process.env.AIMDOC_IDENTITY_SECRET,
)
.update(signedPayload)
.digest('hex')
The key order shown above is part of the canonical representation. Send the same values and the server-generated hash to the browser:
aimdoc.identify({
external_id: user.id,
email: user.email,
account: {
external_id: account.external_id,
name: account.name,
domain: account.domain,
},
user_hash: userHashFromServer,
})
See SDKs for the complete account identity behavior and SDK method usage.
Enable enforcement
After all of your applications send signed user-only identifies, enable Enforce identity verification in the dashboard. Aimdoc will then reject every unsigned identify call.
Roll this out in order:
- Generate and securely store the secret.
- Deploy server-side signature generation.
- Verify your application sends valid hashes.
- Enable enforcement.
Invalid hashes are always rejected, regardless of the enforcement setting.
Rotate the secret
Choose Rotate in the Identity Verification settings when you need to replace a secret. Rotation immediately invalidates signatures generated with the previous secret.
Deploy the new secret to your backend promptly after rotation. Identify calls using the old secret will fail until your server generates hashes with the new one.