Identifying visitors and passing context
How to report signed-in users with ReactiveChat('identify'), sign the userHash with HMAC-SHA256 on your server, rotate the secret and give the assistant structured page context such as the product or the cart.
If your website knows who is there, tell the chat. Your team then sees a name in the desk instead of “Visitor 4f2a”, and features such as customer data and the customer portal become possible in the first place.
The principle: the browser proves nothing. Whatever a page claims via identify, any visitor can fake in the console. A user counts as confirmed only with a signature that your server has computed with a secret that never reaches the browser.
The call in the browser
ReactiveChat('identify', {
userId: '4711',
email: 'kim@example.com',
name: 'Kim Berger',
userHash: '9c1f…',
exp: 1792503600
});
- userId (string, recommended) – your own permanent ID for the user. Not an email address, not a sequential number that can change.
- email (string, optional) – shown in the desk. It is only confirmed if it was signed as well (below).
- name (string, optional) – display name in the desk.
- userHash (string, required for confirmation) – 64 hex characters, computed on your server.
- exp (integer, recommended) – expiry time of the hash in Unix seconds, at most 24 hours in the future.
Other fields are discarded. identify may come before the widget has loaded; it goes into the queue and does not trigger early loading. The values travel along when the connection is established and, if the widget is already connected, immediately.
The secret
In your account under Project → Identity verification you generate the project's secret (permission: edit the project). It consists of 64 hex characters and is shown in full exactly once, after that only masked. Put it in your server's environment – never in a script in the browser, in an app or in a repository.
Important for the computation: the HMAC key is the 64-character string itself, exactly as shown – not the bytes decoded from it.
Computing the signature
userHash = hex( HMAC_SHA256( secret, userId + "." + exp ) )
Lowercase hex, 64 characters. With a confirmed email address:
userHash = hex( HMAC_SHA256( secret, userId + "." + exp + "." + email ) )
Only in the second form does the email address count as confirmed. It requires exp.
PHP:
<?php
$geheim = getenv('RC_IDENT_SECRET'); // 64 hex characters from the account
$userId = (string) $kunde->id;
$exp = time() + 3600; // at most 24 h
$userHash = hash_hmac('sha256', $userId . '.' . $exp, $geheim);
?>
<script nonce="<?= $cspNonce ?>">
ReactiveChat('identify', {
userId: <?= json_encode($userId) ?>,
userHash: <?= json_encode($userHash) ?>,
exp: <?= (int) $exp ?>
});
</script>
Node.js:
const crypto = require('crypto');
function rcIdentitaet(kunde) {
const userId = String(kunde.id);
const exp = Math.floor(Date.now() / 1000) + 3600;
const userHash = crypto.createHmac('sha256', process.env.RC_IDENT_SECRET)
.update(userId + '.' + exp)
.digest('hex');
return { userId, userHash, exp };
}
Python:
import hmac, hashlib, os, time
def rc_identitaet(kunde_id: str) -> dict:
exp = int(time.time()) + 3600
msg = f"{kunde_id}.{exp}".encode()
key = os.environ["RC_IDENT_SECRET"].encode() # the hex string as text
return {"userId": kunde_id, "exp": exp,
"userHash": hmac.new(key, msg, hashlib.sha256).hexdigest()}
With an email address (PHP):
$userHash = hash_hmac('sha256', $userId . '.' . $exp . '.' . $email, $geheim);
The address must be signed exactly as you pass it to identify.
Without an inline script (strict CSP)
If your Content Security Policy does not allow inline scripts, output the values as data and call identify from a script file of your own:
<meta name="rc-ident" content='{"userId":"4711","userHash":"9c1f…","exp":1792503600}'>
// /js/chat-ident.js
var m = document.querySelector('meta[name="rc-ident"]');
if (m) { ReactiveChat('identify', JSON.parse(m.content)); }
In single-page apps, fetch the three values from your server via fetch after sign-in.
What the server checks
expis an integer (Unix seconds), lies in the future and at most 24 hours ahead (plus 5 minutes of tolerance).- The hash matches
userId + "." + exp(or the form with email). Uppercase letters in the hash are tolerated. - The comparison is constant-time.
Confirmed and fresh is a user with a valid hash and exp. Only then do they get customer data and the customer portal.
Older form without exp: userHash = HMAC_SHA256(secret, userId) is still accepted as confirmed, but not as fresh – it is enough for the conversation history and the display in the desk, not for customer data. Switch existing integrations to the form with exp.
When the check fails
The chat keeps working, but anonymously. The widget receives the error auth_failed with a reason in detail.reason:
- no_hash –
userHashis missing. - no_secret – no secret has been set up for the project.
- bad_exp –
expis not a valid integer in seconds (often: milliseconds were passed). - expired –
explies in the past (page open for a long time, wrong server clock). - exp_too_far –
explies more than 24 hours ahead. - bad_hash – the hash does not match: wrong secret, wrong order,
userIdsigned as a number instead of a string, email written differently.
In the network tab of the developer tools, the reason is in the error frame of the WebSocket connection; init_ok.identity shows verified and reason.
There is no “only allow confirmed visitors” setting: unconfirmed visitors always chat as anonymous visitors.
Rotating the secret
- Generate new secret: the previous one stays valid for another 24 hours. Enter the new one on your servers within that time.
- Remove: makes both invalid immediately. All users then count as unconfirmed until a new secret is set up.
Switching users and signing out
- If you call
identifywith a different user (for example after switching accounts in an SPA), the widget separates the input draft from the previous user. - There is no dedicated sign-out command. The identity only applies to the current page view: after sign-out, serve pages without
identify. Conversation and visitor ID are kept in the browser; if a shared device should no longer show anything, the visitor can delete their data in the widget.
Passing context
Besides the identity, your page can tell the assistant and your team what is going on right now – for example which product is shown or what is in the cart. This works via track:
ReactiveChat('track', 'warenkorb', {
page_type: 'cart',
cart_value: 129.80,
cart_items: 3,
currency: 'EUR'
});
Only these fields are taken over; everything else is discarded:
- Page:
url,path,title,referrer,page_type,landing_path - Product:
product_id,variant_id,category,price,currency,in_stock - Cart:
cart_value,cart_items - Campaign:
utm_source,utm_medium,utm_campaign,gclid,msclkid,fbclid - Signals:
intent,intent_confidence,rage_score,scroll_pct,dwell_ms
The widget records address, title, referrer and campaign parameters on every page change by itself anyway. Context is only transmitted with consent (Privacy integration). Do not write personal data into these fields – that is what identify is for.
Mobile apps
In an app, you pass the identity via JavaScript after the WebView has loaded, never through the address. Here too, the hash is computed on your server, not in the app: Mobile apps.
Ultima actualizare: