Use Cases Pricing Docs
EN SL HR
Log in Start Free

Identity Verification

Secure visitor authentication with HMAC

Identity Verification

Identity verification ensures that visitors can only access their own conversations and prevents impersonation. Hal uses HMAC (Hash-based Message Authentication Code) to securely verify visitor identities.

Why Use Identity Verification?

Without verification, anyone who knows a visitor ID could potentially:

  • Access another user's conversation history
  • Impersonate someone else in chat
  • View private information in past messages

Identity verification prevents these security issues by cryptographically signing visitor data with a secret key only your server knows.

How It Works

  1. Get your secret key

    Hal provides a unique secret key for your app in Settings > Security.

  2. Generate HMAC hash

    On your server, create an HMAC hash of the visitor ID using your secret key.

  3. Pass hash to widget

    Include the hash when initializing the Hal widget.

  4. Hal verifies

    Hal validates the hash using your secret key. If it matches, the visitor is authenticated.

Important: Never expose your secret key in client-side code. Always generate the HMAC hash on your server.

Implementation

Step 1: Get Your Secret Key

  1. Go to Settings > Security in your Hal dashboard
  2. Enable "Require Identity Verification"
  3. Copy your secret key (it looks like a long random string)
  4. Store it securely in your server environment variables

Step 2: Generate HMAC Hash on Your Server

Here are examples in different languages:

Node.js:

const crypto = require('crypto');

function generateHalHash(visitorId, secretKey) {
  return crypto
    .createHmac('sha256', secretKey)
    .update(visitorId)
    .digest('hex');
}

// Example usage
const visitorId = 'user-123';
const secretKey = process.env.HAL_SECRET_KEY;
const hash = generateHalHash(visitorId, secretKey);

Python:

import hmac
import hashlib
import os

def generate_hal_hash(visitor_id, secret_key):
    return hmac.new(
        secret_key.encode('utf-8'),
        visitor_id.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

# Example usage
visitor_id = 'user-123'
secret_key = os.environ['HAL_SECRET_KEY']
hash_value = generate_hal_hash(visitor_id, secret_key)

PHP:

<?php
function generateHalHash($visitorId, $secretKey) {
    return hash_hmac('sha256', $visitorId, $secretKey);
}

// Example usage
$visitorId = 'user-123';
$secretKey = getenv('HAL_SECRET_KEY');
$hash = generateHalHash($visitorId, $secretKey);
?>

Ruby:

require 'openssl'

def generate_hal_hash(visitor_id, secret_key)
  OpenSSL::HMAC.hexdigest('SHA256', secret_key, visitor_id)
end

# Example usage
visitor_id = 'user-123'
secret_key = ENV['HAL_SECRET_KEY']
hash = generate_hal_hash(visitor_id, secret_key)

Go:

package main

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

func generateHalHash(visitorID, secretKey string) string {
    h := hmac.New(sha256.New, []byte(secretKey))
    h.Write([]byte(visitorID))
    return hex.EncodeToString(h.Sum(nil))
}

// Example usage
func main() {
    visitorID := "user-123"
    secretKey := os.Getenv("HAL_SECRET_KEY")
    hash := generateHalHash(visitorID, secretKey)
}

Step 3: Pass Hash to Widget

Include the userHash in your widget settings:

<script>
  window.HalSettings = {
    appId: 'your-app-id',
    visitorId: '<%= user.id %>',
    name: '<%= user.name %>',
    email: '<%= user.email %>',
    userHash: '<%= hal_hash %>'  // Generated on your server
  };
</script>
<script src="https://api.chatwithhal.com/widget.js"></script>

Complete Example: Express.js

Here's a full example using Express.js:

const express = require('express');
const crypto = require('crypto');

const app = express();
const HAL_SECRET_KEY = process.env.HAL_SECRET_KEY;

function generateHalHash(visitorId) {
  return crypto
    .createHmac('sha256', HAL_SECRET_KEY)
    .update(visitorId)
    .digest('hex');
}

app.get('/dashboard', (req, res) => {
  const user = req.user; // From your auth middleware

  const halSettings = {
    appId: 'your-app-id',
    visitorId: user.id,
    name: user.name,
    email: user.email,
    userHash: generateHalHash(user.id),
    metadata: {
      plan: user.subscription.plan,
      signupDate: user.createdAt
    }
  };

  res.render('dashboard', { halSettings });
});

In your template:

<script>
  window.HalSettings = <%- JSON.stringify(halSettings) %>;
</script>
<script src="https://api.chatwithhal.com/widget.js"></script>

Testing Identity Verification

Verify It's Working

  1. Enable identity verification in Hal settings
  2. Load your website with the widget
  3. Open browser console and look for Hal widget logs
  4. You should see "Identity verified successfully"

Test Invalid Hash

To verify security is working:

  1. Try using a random string as the userHash
  2. The widget should reject the connection
  3. Console will show "Identity verification failed"

Common Issues

Verification Failing

If identity verification isn't working:

  • Check the secret key - Ensure it matches the one in Hal settings
  • Verify visitor ID - The ID must match exactly (case-sensitive)
  • Character encoding - Use UTF-8 encoding for all strings
  • Hash algorithm - Must use SHA256, not MD5 or other algorithms

Hash Mismatch

Debug hash generation:

// On your server, log the inputs and output
console.log('Visitor ID:', visitorId);
console.log('Secret Key:', secretKey.substring(0, 10) + '...');
console.log('Generated Hash:', hash);

// Compare with what Hal expects in Settings > Security > Test Verification

Security Best Practices

1. Protect Your Secret Key

  • Store in environment variables, not in code
  • Never commit to version control
  • Rotate regularly (every 90 days)
  • Use different keys for development and production

2. Generate Hash Server-Side

Always generate the HMAC hash on your backend:

  • ❌ Don't: Generate in browser JavaScript
  • ❌ Don't: Send secret key to the client
  • ✅ Do: Generate on your server
  • ✅ Do: Send only the hash to the client

3. Validate User Sessions

Only generate Hal hashes for authenticated users:

// Check user is authenticated first
if (!req.user || !req.user.id) {
  return res.status(401).send('Unauthorized');
}

// Then generate hash
const hash = generateHalHash(req.user.id, HAL_SECRET_KEY);

4. Use Consistent Visitor IDs

Use the same visitor ID format throughout your application:

  • User database ID (recommended)
  • Email address (less recommended, can change)
  • External system ID

Avoid:

  • Session IDs (change on logout)
  • Randomly generated values
  • Hashed or encrypted values

Rotating Your Secret Key

If you need to change your secret key:

  1. Generate a new key in Settings > Security
  2. Update the key in your server environment variables
  3. Deploy the change to all servers
  4. Wait 24 hours for all sessions to refresh
  5. Verify no verification errors in your logs
Note: During rotation, Hal accepts both old and new keys for 24 hours to prevent disruption.

Anonymous vs. Verified Users

You can support both anonymous and verified users:

  • Logged-out visitors - Don't provide visitorId or userHash. Hal generates anonymous ID.
  • Logged-in users - Provide visitorId and userHash for verified identity.

Example implementation:

<script>
  window.HalSettings = {
    appId: 'your-app-id',
    <% if (user) { %>
    visitorId: '<%= user.id %>',
    name: '<%= user.name %>',
    email: '<%= user.email %>',
    userHash: '<%= hal_hash %>',
    <% } %>
  };
</script>

When to Use Identity Verification

Always use for:

  • SaaS applications with user accounts
  • Banking or financial services
  • Healthcare or medical applications
  • E-commerce with order history
  • Any app with private user data

Optional for:

  • Public marketing websites
  • Anonymous support chat
  • Pre-login conversations

Next Steps