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
-
Get your secret key
Hal provides a unique secret key for your app in Settings > Security.
-
Generate HMAC hash
On your server, create an HMAC hash of the visitor ID using your secret key.
-
Pass hash to widget
Include the hash when initializing the Hal widget.
-
Hal verifies
Hal validates the hash using your secret key. If it matches, the visitor is authenticated.
Implementation
Step 1: Get Your Secret Key
- Go to Settings > Security in your Hal dashboard
- Enable "Require Identity Verification"
- Copy your secret key (it looks like a long random string)
- 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
- Enable identity verification in Hal settings
- Load your website with the widget
- Open browser console and look for Hal widget logs
- You should see "Identity verified successfully"
Test Invalid Hash
To verify security is working:
- Try using a random string as the
userHash - The widget should reject the connection
- 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:
- Generate a new key in Settings > Security
- Update the key in your server environment variables
- Deploy the change to all servers
- Wait 24 hours for all sessions to refresh
- Verify no verification errors in your logs
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