Identify signed-in users
Tell Giov Copilot who is signed in with a userHash from your server, keep it in sync in single-page apps, and pass API tokens for browser actions.
On this page
Copilot only switches on for a user your server vouches for. Your server signs the user's ID with your workspace identity secret, and your page passes the ID and the signature (userHash) to the widget. Giov checks the hash before Copilot starts. Without a valid hash, the widget stays in support mode.
This is the same check that Identify visitors uses for the support widget. It uses the same secret, so if you already identify visitors, step 2 of the Copilot setup is done.
Your Copilot setup
Sign in to see your workspace's plan, paths, identity check and the setup steps left to do.
1. Create the identity secret
Open Settings → Chat widget → Visitor details, turn on Signed-in users and select Generate secret (or rotate an existing one). Store it in your server configuration, for example as GIOV_IDENTITY_SECRET.
Keep the secret on your server. Never put it in browser code, a frontend .env file or your repository. Anyone who has it can sign in as any of your users.
2. Compute userHash on your server
userHash is the HMAC-SHA256 of the user's ID, as lowercase hex, keyed with the secret exactly as issued. Use the same ID you pass to the widget.
import { createHmac } from "node:crypto";
const userHash = createHmac("sha256", process.env.GIOV_IDENTITY_SECRET)
.update(String(user.id))
.digest("hex");import hashlib, hmac, os
user_hash = hmac.new(
os.environ["GIOV_IDENTITY_SECRET"].encode(),
str(user.id).encode(),
hashlib.sha256,
).hexdigest()<?php
$userHash = hash_hmac('sha256', (string) $user->id, getenv('GIOV_IDENTITY_SECRET'));require "openssl"
user_hash = OpenSSL::HMAC.hexdigest("SHA256", ENV["GIOV_IDENTITY_SECRET"], user.id.to_s)import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"os"
)
mac := hmac.New(sha256.New, []byte(os.Getenv("GIOV_IDENTITY_SECRET")))
mac.Write([]byte(userID))
userHash := hex.EncodeToString(mac.Sum(nil))3. Pass it to the widget
Render the user's ID and hash into your dashboard page. Don't load a second widget; add them to the installation you already have.
If you install with the script tag, set window.GiovChatSettings before it. If you call ChatWidget.init yourself, add the values to its metadata instead.
<script>
window.GiovChatSettings = {
userId: "USER_ID", // the same id your server hashed
userHash: "USER_HASH", // computed on your server, never in the browser
firstName: "FIRST_NAME" // optional, so Copilot greets people by name
};
</script>
<script src="https://widget.giovchat.app/YOUR_WORKSPACE_ID" async></script>ChatWidget.init({
tenantId: "YOUR_WORKSPACE_ID",
metadata: {
userId: "USER_ID", // the signed-in user's id
userHash: "USER_HASH", // computed on your server, never in the browser
firstName: "FIRST_NAME", // optional, so Copilot greets people by name
},
});tenantId is your workspace ID. Giov Copilot → Website setup shows this snippet with your workspace ID already filled in.
On a dashboard path, a verified user now gets Copilot. Signed-out users and public pages keep the support chat.
Single-page apps
If users sign in or out without a full page reload, tell the widget when that happens:
// Single-page apps: after the user signs in
ChatWidget.identify({ userId, userHash, firstName }); // firstName: optional, for the greeting
// On sign-out
ChatWidget.identify(null);
await ChatWidget.resetSession();identify({ userId, userHash, firstName })verifies the new user and switches to Copilot on dashboard paths. Fetch theuserHashfrom your server after sign-in.firstNameis optional and display-only, so Copilot greets people by name. A different user never inherits the previous name.identify(null)returns the widget to support mode.resetSession()clears Copilot's state in the browser straight away, then ends the session with Giov. Call it before sign-out or an account switch, so the next user never sees the previous user's Copilot.
Copilot sessions are kept per browser tab.
API token headers
Copilot calls your API from the user's browser and sends their cookies. If your API reads a header token such as Authorization instead of a cookie, register it once:
// Only if your API authenticates with a header token instead of cookies.
// Copilot calls your API from the user's browser, so it needs the same header.
ChatWidget.setAuthHeaders(() => ({
Authorization: `Bearer ${getAccessToken()}`,
}));- The function is called for each browser action, so return the current token, and refresh it the way your app normally does. It can also return a promise.
- The headers are kept in memory only. They're never logged or stored.
- They're sent only with Copilot's browser actions, to your approved origins.
- Call
ChatWidget.setAuthHeaders(null)to remove them, for example on sign-out.
What a userHash identity can do
A userHash proves who the user is, but it doesn't carry roles or scopes. With it, Copilot can:
- run browser actions, which your API authorizes with the user's own session.
It can't:
- use server connections, which act with your backend's credentials;
- run actions that require scopes.
For those, use the signing-key identity.
Signing-key identity (Advanced)
The signing-key identity is for apps that need roles and scopes, or server connections. Your backend signs a short-lived proof of who is signed in, with a private key only it holds. Copilot checks the proof with the matching public key.
To set it up:
- In Giov Copilot → Advanced, turn on Use a signing-key session endpoint and generate a signing key. Giov keeps only the public key. Copy the private key once and store it as a server-only secret named
COPILOT_HOST_PRIVATE_JWK. - Add a same-origin endpoint to your backend,
POST /api/giov-sessionby default. It checks your existing login and returns{ "assertion": "…" }, plus an optionaluser: { "firstName": "…" }so Copilot greets people by name. The assertion is an ES256-signed token that expires within 60 seconds and lists the user's account, roles and scopes. For a signed-out user it returns a JSON401, not a redirect to your login page. - Call
ChatWidget.refreshSession()after a new login, andChatWidget.resetSession()before logout or an account switch.
Publishing a new public key ends existing Copilot sessions, so deploy the new private key at the same time.
Public documentation for the signing-key identity is coming. Until then, Giov Copilot → Developer guide in your dashboard has the full reference: the token's claims, endpoint examples, revocation and troubleshooting.