Overview

Customer Identity Verification allows you to connect your existing authentication system to cStar. When enabled, your customers can use their existing account to access support—no separate cStar login required.

Benefits

  • Single Sign-On Experience: Customers use their existing account
  • Cross-Device History: Conversations follow customers across devices
  • Richer Context: Pass customer metadata (plan, account age, etc.) to agents
  • Verified Profiles: Know exactly who you're talking to

How It Works

  1. Your server generates an HMAC-SHA256 signature of the customer's identity
  2. Your frontend passes this signature to the cStar widget
  3. cStar verifies the signature and creates/links the customer record
  4. Customer sees their full conversation history
Your Server ──(signature)──> Your Frontend ──(signature)──> cStar Widget
                                                              │
                                                              ▼
                                                         Verification
                                                              │
                                                              ▼
                                                    ✓ Authenticated Chat

Setup Guide

Step 1: Enable in Settings

  1. Go to Settings → Widget → Customer Identity Verification
  2. Toggle Enable Identity Verification
  3. Copy your Production Key (sk_live_...) and Test Key (sk_test_...)
  4. Store the production key securely on your server (never expose to frontend!)

Step 2: Create Backend Endpoint

Create an API endpoint that generates signed customer data:

Node.js / Express:

const crypto = require('crypto');

app.get('/api/cstar-identity', (req, res) => {
  // Must be authenticated in your system
  const user = req.session.user;
  if (!user) {
    return res.status(401).json({ error: 'Not logged in' });
  }

  // Only these fields are signed (flat, no nested objects)
  const signedFields = {
    email: user.email,
    externalId: String(user.id),
    name: user.name,
    timestamp: Math.floor(Date.now() / 1000)
  };

  // Remove undefined, sort keys alphabetically
  const filtered = Object.fromEntries(
    Object.entries(signedFields)
      .filter(([_, v]) => v !== undefined)
      .sort(([a], [b]) => a.localeCompare(b))
  );

  // Generate HMAC signature
  const payload = JSON.stringify(filtered);
  const signature = crypto
    .createHmac('sha256', process.env.CSTAR_IDENTITY_SECRET)
    .update(payload)
    .digest('hex');

  res.json({ customer: filtered, signature });
});

Python / Flask:

import hmac, hashlib, json, time
from flask import jsonify, session

@app.route('/api/cstar-identity')
def cstar_identity():
    user = session.get('user')
    if not user:
        return jsonify({'error': 'Not logged in'}), 401

    signed_fields = {
        'email': user['email'],
        'externalId': str(user['id']),
        'name': user.get('name'),
        'timestamp': int(time.time())
    }

    # Remove None values, sort keys
    signed_fields = {k: v for k, v in sorted(signed_fields.items()) if v is not None}

    payload = json.dumps(signed_fields, separators=(',', ':'))
    signature = hmac.new(
        CSTAR_SECRET.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    return jsonify({'customer': signed_fields, 'signature': signature})

Ruby / Rails:

def identity
  return render json: { error: 'Not logged in' }, status: 401 unless current_user

  signed_fields = {
    email: current_user.email,
    externalId: current_user.id.to_s,
    name: current_user.name,
    timestamp: Time.now.to_i
  }.compact.sort.to_h

  payload = signed_fields.to_json
  signature = OpenSSL::HMAC.hexdigest(
    'SHA256',
    ENV['CSTAR_IDENTITY_SECRET'],
    payload
  )

  render json: { customer: signed_fields, signature: signature }
end

PHP / Laravel:

public function cstarIdentity(Request $request)
{
    $user = auth()->user();
    if (!$user) {
        return response()->json(['error' => 'Not logged in'], 401);
    }

    $signedFields = array_filter([
        'email' => $user->email,
        'externalId' => (string) $user->id,
        'name' => $user->name,
        'timestamp' => time(),
    ], fn($v) => $v !== null);

    ksort($signedFields);

    $payload = json_encode($signedFields, JSON_UNESCAPED_SLASHES);
    $signature = hash_hmac('sha256', $payload, env('CSTAR_IDENTITY_SECRET'));

    return response()->json([
        'customer' => $signedFields,
        'signature' => $signature,
    ]);
}

Step 3: Update Frontend Widget

async function initCStarWidget() {
  const isLoggedIn = !!currentUser;

  if (isLoggedIn) {
    // Fetch signed identity from your backend
    const { customer, signature } = await fetch('/api/cstar-identity')
      .then(r => r.json());

    window.cStarConfig = {
      teamSlug: 'your-team-slug',
      customer,
      signature
    });
  } else {
    // Unauthenticated - show login prompt
    window.cStarConfig = {
      teamSlug: 'your-team-slug',
      loginRedirectUrl: '/login?redirect=' + encodeURIComponent(window.location.pathname)
    });
  }
}

// Call on page load
initCStarWidget();

// Call cStar.logout() when user logs out
function handleLogout() {
  cStar.logout();
  // ... rest of your logout logic
}

Test Mode

Use test mode during development for easier debugging:

  1. Use your Test Key (sk_test_...) on your backend
  2. Add testMode: true to widget initialization
  3. Timestamp validation is relaxed (1 hour instead of 5 minutes)
  4. Detailed error messages appear in console
window.cStarConfig = {
  teamSlug: 'your-team-slug',
  testMode: true,  // Relaxed validation for development
  customer,
  signature
});

Remove testMode: true before going to production!


Key Rotation

Keys can be rotated at any time from Settings. When you rotate keys:

  • 24-hour grace period: Both old and new keys work for 24 hours
  • Zero-downtime updates: Update your servers without breaking existing sessions
  • Automatic fallback: cStar tries the new key first, then falls back to the previous key

When to rotate:

  • Suspected key compromise
  • Team member with access leaves
  • Regular security rotation (recommended: quarterly)

Signature Testing Tool

Use the built-in testing tool to verify your integration:

  1. Go to Settings → Widget → Customer Identity Verification
  2. Click Test Your Integration
  3. Paste your customer JSON and signature
  4. See instant feedback on whether verification would succeed

Error Codes

Error CodeCauseSolutionINVALID_SIGNATUREHMAC doesn't matchCheck your secret key and JSON serializationSIGNATURE_EXPIREDTimestamp > 5 min oldGenerate fresh signatures on each page loadMISSING_REQUIRED_FIELDNo externalId or emailInclude all required fieldsRATE_LIMITEDToo many failed attemptsWait and retry, check for bugs


Troubleshooting

"Signature is invalid"

  • Ensure you're sorting object keys alphabetically before signing
  • Check that you're using JSON.stringify() with no extra whitespace
  • Verify you're using the correct key (test vs. production)

"Signature expired"

  • Generate signatures server-side on each page load, not cached
  • Ensure your server clock is synchronized (NTP)
  • In test mode, timestamps are valid for 1 hour

"Customer not linked"

  • External IDs are case-sensitive
  • The first verified login creates the link—subsequent logins update it

Security Notes

  • Never expose your secret key to the frontend
  • Signatures include timestamps to prevent replay attacks
  • Rate limiting protects against brute-force signature guessing
  • Use HTTPS for all API calls