Skip to content

Identity verification

By default, anyone visiting your page is anonymous to the chat. To bind a chat to a real account in your system, sign the user's id with a per-widget secret on your server.

When to use

  • You have a logged-in product and want to carry that identity into chat.
  • You need returning visitors to land in the same conversation history across devices.
  • You gate operator workflows on verified identity.

If your visitors are anonymous, skip this and use the Pre-chat form instead.

How it works

  1. Inviaro generates a widget_secret per widget connection, stored encrypted at rest. You read it once from the admin and put it in your server's environment.
  2. When rendering the page for a logged-in user, your server computes user_hash = hmac_sha256(user_id, widget_secret) and embeds the digest in the page HTML.
  3. The widget sends user_id and user_hash on every inbound message.
  4. Inviaro recomputes the digest with the stored secret and compares timing-safely. On match, the contact is marked verified.
  5. On mismatch, the auth claim is silently dropped. Chat continues anonymously without an error to the visitor.

Server-side examples

Node

js
const crypto = require('node:crypto');

function inviaroUserHash(userId) {
  if (!userId) throw new Error('userId required');
  return crypto
    .createHmac('sha256', process.env.INVIARO_WIDGET_SECRET)
    .update(userId)
    .digest('hex');
}

app.get('/api/me', (req, res) => {
  const user = req.session.user;
  res.json({
    id: user.id,
    email: user.email,
    name: user.name,
    inviaroUserHash: inviaroUserHash(user.id)
  });
});

PHP

php
function inviaro_user_hash(string $userId): string {
    return hash_hmac('sha256', $userId, getenv('INVIARO_WIDGET_SECRET'));
}

Python

python
import hmac, hashlib, os

def inviaro_user_hash(user_id: str) -> str:
    secret = os.environ['INVIARO_WIDGET_SECRET'].encode()
    return hmac.new(secret, user_id.encode(), hashlib.sha256).hexdigest()

Ruby

ruby
require 'openssl'

def inviaro_user_hash(user_id)
  OpenSSL::HMAC.hexdigest('sha256', ENV.fetch('INVIARO_WIDGET_SECRET'), user_id.to_s)
end

Go

go
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "os"
)

func InviaroUserHash(userId string) string {
    h := hmac.New(sha256.New, []byte(os.Getenv("INVIARO_WIDGET_SECRET")))
    h.Write([]byte(userId))
    return hex.EncodeToString(h.Sum(nil))
}

Wiring the page

html
<script>
  window.InviaroSettings = {
    key: 'wt_...',
    origin: 'https://yourdomain.com',
    userId: '<%= current_user.id %>',
    userHash: '<%= inviaro_user_hash(current_user.id) %>',
    userEmail: '<%= current_user.email %>',
    userName: '<%= current_user.name %>'
  };
</script>
<script src="https://widget.inviaro.com/v1.js" async></script>

Runtime updates

When a visitor logs in mid-session in a single-page app:

js
Inviaro('identify', {
  userId: 'usr_42',
  userHash: 'a1b2c3...',
  email: 'amar@example.com',
  name: 'Amar'
});

When they log out:

js
Inviaro('logout');

Security

  • The widget_secret lives only on your server. Never put it in client JavaScript.
  • Authenticate the request before signing. Never expose an unauthenticated endpoint that returns user_hash for arbitrary inputs.
  • Use a stable user_id (UUID or database primary key). Treat it as a stable identifier, not an email or username.
  • Rotate the secret if it leaks. Old digests fail verification immediately.

WARNING

Do not compute the hash in client-side JavaScript. The secret would be extractable on first page load.

Verification result

When verification succeeds:

  • The contact's is_verified field becomes true in the inbox.
  • Returning visits with the same user_id are folded into the same contact, preserving conversation history across logins on different browsers or devices.

When verification fails:

  • The contact stays anonymous.
  • Chat itself continues working.
  • A warning is logged server-side for your support team.