Secure your identify() call
Add a server-side HMAC signature so DropFix can verify that user identity data has not been tampered with.
HMAC verification
How it works
Without a signature, a user can open their browser DevTools and call identify() with any plan or MRR they want. DropFix would accept it.
With HMAC, your server signs the user ID with a secret key before sending it to the browser. DropFix recomputes the same signature server-side and rejects the call if the values do not match. The secret key never leaves your server.
Without HMAC
Any user can fake their own plan or MRR
With HMAC
Plan and MRR are cryptographically verified
Without HMAC
Identity data is trusted on the honour system
With HMAC
DropFix rejects tampered identify() calls
Without HMAC
Signals and digests may be based on wrong data
With HMAC
All churn signals use verified data only
| Without HMAC | With HMAC |
|---|---|
| Any user can fake their own plan or MRR | Plan and MRR are cryptographically verified |
| Identity data is trusted on the honour system | DropFix rejects tampered identify() calls |
| Signals and digests may be based on wrong data | All churn signals use verified data only |
Step 1 — Generate the signature on your server
On your backend, after login succeeds, generate an HMAC-SHA256 signature of the user ID using your DropFix secret key. Include a Unix timestamp so the signature expires after 5 minutes.
import { createHmac } from 'crypto'
const signed_at = Math.floor(Date.now() / 1000)
const dropfix_hash = createHmac('sha256', process.env.DROPFIX_SECRET_KEY)
.update(user.id)
.digest('hex')
// Return both to the frontend
return { user: { ...user, dropfix_hash, dropfix_signed_at: signed_at } }import hmac, hashlib, os, time
signed_at = int(time.time())
dropfix_hash = hmac.new(
os.environ['DROPFIX_SECRET_KEY'].encode(),
user.id.encode(),
hashlib.sha256
).hexdigest()
# Return both to the frontend
return { 'dropfix_hash': dropfix_hash, 'dropfix_signed_at': signed_at }signed_at = Time.now.to_i
dropfix_hash = OpenSSL::HMAC.hexdigest(
'SHA256',
ENV['DROPFIX_SECRET_KEY'],
user.id.to_s
)
# Return both to the frontend
render json: { dropfix_hash: dropfix_hash, dropfix_signed_at: signed_at }Where to find your secret key
Go to DropFix -> Settings -> Security -> Secret Key. Store it in your server environment asDROPFIX_SECRET_KEY. Never put it in client-side code or commit it to version control.Step 2 — Pass the signature in identify()
Return dropfix_hash and dropfix_signed_at from your login API and pass them to identify().
DropFix.identify(user.id, {
name: user.name,
email: user.email,
plan: user.plan,
mrr: user.mrr,
signup_date: user.createdAt,
signature: user.dropfix_hash, // or hash: user.dropfix_hash
signedAt: user.dropfix_signed_at,
})Signature expires after 5 minutes
ThesignedAt timestamp lets DropFix reject signatures older than 5 minutes. Generate a fresh signature on every login — do not cache it between sessions.What happens when verification fails
Situation
No signature sent
DropFix response
Request accepted — user stored as unverified
Situation
Wrong signature
DropFix response
401 returned — nothing stored
Situation
Signature older than 5 minutes
DropFix response
401 returned — nothing stored
Situation
Valid signature
DropFix response
200 returned — plan and MRR trusted and stored
| Situation | DropFix response |
|---|---|
| No signature sent | Request accepted — user stored as unverified |
| Wrong signature | 401 returned — nothing stored |
| Signature older than 5 minutes | 401 returned — nothing stored |
| Valid signature | 200 returned — plan and MRR trusted and stored |
Enable strict mode in Settings -> Security to reject all identify() calls that do not include a valid signature.