Identify signed-in users with JWT
If your app already knows who the user is — they’re signed in — pass that identity into the chatbot widget so it can personalize replies and avoid asking again. JWT user identification uses an HMAC-signed token your backend generates, which the Hilal Chatbot backend verifies before trusting the claims.
In this guide:
- Why use verified identity
- The three identity states
- Configure a per-bot HMAC secret
- Generate a JWT in your backend
- Pass the token to the widget
- Verify it’s working
- Security notes
Why use verified identity
Without JWT, the widget treats users as either:
- Anonymous — no name, email, or ID known.
- Self-reported — your host page passed
data-user-name/data-user-email, but anyone could fake those.
With JWT:
- Verified — the user’s identity is cryptographically signed by your backend with a secret only you and Hilal Chatbot share. The chatbot trusts these claims for personalization, the agent dashboard shows the verified badge, and the chat API records
X-Hilal-Identity-Source: verifiedon every message.
Step 1: Configure the HMAC secret
In your chatbot detail → Basic / Appearance (look for the Widget Security card):
- Click Regenerate to create a fresh 64-character hex secret.
- Copy and store it somewhere your backend can read (env var, secrets manager).
- Click Save.
The secret is stored encrypted at rest. The UI shows a masked preview only — the full value is shown once when generated, so copy it now.
Screenshot: The Widget Security card with the HMAC secret regeneration UI.
Single-tenant fallback: if you don’t yet have backend support for a per-bot secret, you can set the env var
WIDGET_JWT_HMAC_SECRETon your Hilal Chatbot deployment instead. The per-bot secret takes precedence when configured.
Step 2: Generate a JWT in your backend
Use any standard JWT library. Sign with HS256 and your secret. Required claims:
sub— your internal user ID.exp— expiration timestamp (Unix seconds). Recommend ≤ 1 hour from issue.
Recommended additional claims:
name— user’s display name.email— user’s email.
Node.js example:
const jwt = require('jsonwebtoken')
function widgetTokenFor(user) {
return jwt.sign(
{
sub: user.id,
name: user.fullName,
email: user.email,
exp: Math.floor(Date.now() / 1000) + 3600
},
process.env.HILAL_WIDGET_HMAC_SECRET,
{ algorithm: 'HS256' }
)
}Python example:
import jwt, time
def widget_token_for(user):
return jwt.encode(
{
"sub": user.id,
"name": user.full_name,
"email": user.email,
"exp": int(time.time()) + 3600,
},
os.environ["HILAL_WIDGET_HMAC_SECRET"],
algorithm="HS256",
)Generate the token server-side, render it into your page (or fetch it via an authenticated endpoint), and pass it to the widget.
Step 3: Pass the token to the widget
Two ways:
Via embed <script> data attribute:
<script
src="https://cdn.hilalsoftware.tools/chat_v3.js"
data-org-id="org_..."
data-bot-id="bot_..."
data-user-token="eyJhbGciOiJIUzI1NiIs..."
></script>Via init() config:
window.HilalChat.init({
organizationId: 'org_...',
botId: 'bot_...',
userToken: 'eyJhbGciOiJIUzI1NiIs...'
});When the widget mounts, it POSTs the token to /api/widget/verify-jwt. The backend verifies the signature using the bot’s HMAC secret. On success, the verified claims (sub, name, email) flow into the chat session as the user identity.
Step 4: Verify it’s working
Open your site in DevTools → Network:
- Find the
POST /api/widget/verify-jwtrequest after page load. Status 200, response{ verified: true, claims: { sub, name, email } }. - After opening the chat and sending a message, the chat API request includes the header
X-Hilal-Identity-Source: verified. - In the agent dashboard, the conversation row shows the verified badge.
If verification fails:
| Reason | Meaning |
|---|---|
invalid_signature | Wrong secret used to sign, or token tampered with. |
expired | The exp claim is in the past. Issue fresh tokens (≤ 1 hour). |
not_configured | The bot has no HMAC secret set. Configure one. |
malformed | Token isn’t a valid JWT structure. |
The widget falls back gracefully — invalid tokens get treated as anonymous, the chat still works.
Security notes
- Never expose the secret client-side. Sign tokens server-side only. A leaked secret means anyone can forge identities.
- Rotate periodically. Click Regenerate quarterly or after any suspected leak. Existing tokens immediately fail.
- Short
expclaims. A 1-hour token is fine for most apps. Longer tokens widen the leak window. - HS256 only. RS256 / ES256 / asymmetric algorithms are not supported.
alg: noneis rejected unconditionally. - Don’t log tokens. Treat them like passwords.
- HTTPS only. Tokens travel from your backend to the user to Hilal Chatbot — over HTTPS only. The browser blocks mixed-content.
Troubleshooting
- Always falls back to anonymous. Open Network → check the
verify-jwtresponse. Common causes: wrong secret, expiredexp, mis-typedalg. - Verified locally, anonymous in prod. You configured the secret on the dev bot, not the prod bot. Each chatbot has its own secret.
- Used to work, suddenly fails. Did someone rotate the secret? Coordinate with whoever owns Hilal Chatbot in your team.