User verification at a glance
- Your server signs a user token with HS256, keyed on the text of your signing secret.
- Your page passes the token to
identify()asuserToken, in place ofuserIdandtenantId. - The secret stays on your server, out of your pages, your repository and any prompt for an AI coding agent.
- Once you turn on Only allow verified users, HelpAlive refuses every request without a valid user token.
- Setup takes your developer about 15 minutes, and everything is also on Settings → Setup & API Key, step Verify your users.
Prerequisites
- HelpAlive is installed and your pages call
identify(). See Install the script. - A server that knows who is signed in.
- Your developer, about 15 minutes.
The rules
- Sign the token on your server, never in the browser.
- Sign it with HS256, keyed on the text of your signing secret.
- Put the same id your page passes as
userIdinsub, as a string. - Put the same id your page passes as
tenantIdintenant, as a string. Leavetenantout if your app has no companies or workspaces. - Don’t add
exp,kidor any other claim. - Send the token only to the signed-in user’s own page, with
Cache-Control: no-store. - Pass the token to
identify()asuserToken, in place ofuserIdandtenantId. - Keep the secret out of your pages, your repository and any prompt for an AI coding agent.
- Keep
reset()in your logout code, apart fromidentify(), as before.
How to set up user verification
1
Create your signing secret
On Settings → Setup & API Key, step Verify your users, click Create your signing secret. Keep it on your server, like a password, wherever you keep other secrets. The code below reads it from
HELPALIVE_SIGNING_SECRET; the name is up to you.2
Make a user token on your server
Where your server knows the signed-in user, add these lines. They make the user token, which refers to a short signed text that says who the user is. On a CommonJS server, write The token is a JWT signed with HS256, keyed on the text of your secret:
user stands for your app’s signed-in user.- Node.js
- Python
- Ruby
- PHP
- Java
- Go
- C#
- Edge / Deno / Bun
const jwt = require("jsonwebtoken") in place of the import line.Leave out an expiry (
exp) and a key id (kid). HelpAlive honors an exp if you add one, but a token that expires stops working in a tab left open. Rotating the secret is how you cut old tokens off.3
Get the token to the page
Send the token only to the page of the user it names. Print it into the page from your server template, or add it to an endpoint your app already has for the current user. Send that response with
Cache-Control: no-store.4
Pass the token to identify()
Replace your If you still pass a
identify() call with this one. The token says who the user is and which company they belong to, so you no longer pass userId or tenantId.userId or tenantId that differs from the token, the token wins and the browser console shows a warning. A value that isn’t a JWT is ignored, with an error in the console. That catches the secret pasted in by mistake.Your logout code doesn’t change. Keep reset() there, apart from identify(), never straight after it:reset() also drops the token, so the next user on the same browser never inherits it.5
Add the secret to your environment
Add
HELPALIVE_SIGNING_SECRET to your server’s environment, with the secret from step 1 as its value. If your project keeps a list of variables, such as .env.example, add the name there with no value.With Google Tag Manager
If you installed HelpAlive with Google Tag Manager, pass the token through the data layer. Create a Data Layer Variable namedhelpalive_user_token, then add these lines:
With an AI coding agent
The Verify your users step has a prompt for Claude Code, Codex, Cursor or GitHub Copilot under With an AI coding agent. It makes the token on your server, passes it toidentify(), and lists HELPALIVE_SIGNING_SECRET with no value. You set the value yourself, so the secret never goes into a prompt. The prompt is also on Install with an AI coding agent.
Check it works
- Deploy your change, sign in to your app and open the agent.
- On Settings → Setup & API Key, step Verify your users, press refresh on Check your setup. It looks for a verified sign-in in the last 30 minutes. Your signing works means that it found one.
- The Verified sign-ins, last 7 days card shows the share of sign-ins that arrived verified.
Only allow verified users
Until you turn this on, everyone can see the agent. Turn it on once every page that callsidentify() sends a token.
1
Wait for verified sign-ins
The switch unlocks once verified sign-ins arrive. It turns on only after a verified sign-in in the last 30 minutes, so sign in to your app and open the agent first.
2
Turn on Only allow verified users
It sits under the Verified sign-ins, last 7 days card. Recommended on the switch means that nearly all sign-ins are verified. The switch also says how many sign-ins it would have refused in the last 7 days.
3
Confirm
It asks first: “Hide the agent from anyone we can’t verify?” The dialog says how many people in the last 7 days had no token, or a token that had expired or didn’t check out. Press Turn it on anyway, or Show me the fix to open the setup instructions instead.
- A page without a valid token gets no chat button, so the user doesn’t see the agent.
- If a token expires while the chat is open, HelpAlive calls your
refreshUserTokenfunction, if you pass one toidentify(), and sends the request again. If that fails, the user sees “Your session needs a refresh. Reload the page to keep chatting.” with a Reload page button. - If an open chat is refused for any other reason, the question box gives way to “Chat isn’t available right now. Please try again later.” A running assist stops quietly.
- Activity from pages without a token is refused too. Those users stop showing in Reports, Audience and the setup steps.
How to rotate the secret
Click Rotate next to your secret and put the new one inHELPALIVE_SIGNING_SECRET on your server. Nothing in your code changes.
The previous secret keeps working until you rotate again, so nobody is refused while you roll it out. If a secret leaked, rotate once more after your server uses the new one, and the leaked one stops working. Then put that newest secret on your server too, or the next rotation will cut your server off.
Why does the card say no verified sign-in when the server signs tokens?
The check moves only when the agent is used. Sign in, open the agent, and press refresh. Then check that the page passesuserToken to identify(), and that the response carrying it isn’t cached.
Why don’t some users see the chat button?
Some users don’t see the chat button because Only allow verified users is on, and their page sent no valid token. Step 4 on Settings → Setup & API Key names the main reason and the fix. Check that every page that callsidentify() sends a token, or turn the switch off while you fix it.
Why do users see Your session needs a refresh?
Users see “Your session needs a refresh” because their token expired and could not be renewed. PassrefreshUserToken to identify() so HelpAlive can get a fresh one, or leave the expiry out of the token.
Why does the console say userToken is not a JWT?
The console says this when the page passed something other than a signed token, often the secret itself. Pass the token your server made, and rotate the secret if it reached a page.Next
Identify users
Every identify() field.
Security overview
Public keys, secret keys and sessions.
Troubleshooting
The failures that show no error.

