Skip to content
Widget SDK

Identify Signed-in Visitors

Prove who a signed-in visitor is with a short-lived identity token signed on your server.

On this page

Identify Signed-in Visitors#

When a shopper or member is signed in to your site, your chatbot can answer questions only that person should see — “where is my order?”, “what’s on my account?” — without asking them to prove who they are again. To do that safely, your website tells Gydr who is signed in with a short-lived identity token that only your server can create.

You need this only if a connected plugin looks up personal data (for example WooCommerce order status). A chatbot that only answers general questions works without it.

Running WordPress or WooCommerce?

Skip this page and follow the WordPress & WooCommerce setup — seven copy-and-paste steps with a ready-made plugin file, no JWT library needed.

How the token works#

  1. You create an identity key in Console → Settings → Identity verification. The key has an id (the kid, e.g. idk_7Qm2) and a secret. The secret is shown once — store it in your server’s configuration.
  2. When a visitor is signed in, your server signs a small JSON Web Token (JWT, HS256) with that secret, naming the user and your Gydr account.
  3. The widget fetches the token from your site, keeps it in memory, and sends it with each chat message. It never puts it in a URL or in browser storage.
  4. Gydr checks the signature, the account and the expiry. A valid token proves who the visitor is; anything else is treated as a guest.
Sign tokens only on your server. Anyone holding the secret can sign in as any of your users. Never put it in browser JavaScript, never send it to the page, never commit it to a code repository. See Guests & data never to send.

The token#

A standard JWT: a header, a payload of claims, and a signature.

Header
{ "alg": "HS256", "typ": "JWT", "kid": "idk_7Qm2" }
Payload
{
  "sub": "42",
  "aud": "<your Gydr account id>",
  "iat": 1790000000,
  "exp": 1790003600,
  "email": "siti@example.com",
  "email_verified": false,
  "name": "Siti Rahman",
  "attrs": { "crm_id": "c_91" }
}

Claims#

Claim
sub
Required
Yes
What to put in it
Your user id for this person — 1 to 255 printable characters. It must never change for the same person.
Claim
aud
Required
Yes
What to put in it
Your Gydr account id, shown in Console → Settings → Identity verification.
Claim
iat
Required
Yes
What to put in it
When you signed it, in whole Unix seconds. Most JWT libraries add it for you.
Claim
exp
Required
Yes
What to put in it
When it expires, in whole Unix seconds. At most 24 hours after iat; 1 hour is recommended.
Claim
email
Required
No
What to put in it
The person's email address.
Claim
email_verified
Required
No
What to put in it
true ONLY if your site confirmed the person owns that address (for example with a confirmation link). Plugins use the email only when this is true.
Claim
name
Required
No
What to put in it
The person's display name.
Claim
phone_number
Required
No
What to put in it
A phone number, if a plugin needs it.
Claim
attrs
Required
No
What to put in it
An object of extra ids other plugins need, e.g. a CRM id. Plain values only (text, numbers, true/false).

Limits Gydr enforces

  • The whole token is at most 8 KB, with at most 50 claims, each at most 256 characters.
  • Claims are plain values only — text, numbers, true/false — also inside attrs. No lists or nested objects.
  • alg must be HS256. Tokens carrying a crit, jku, jwk, x5u or x5c header are refused.
  • Up to 60 seconds of clock difference is allowed.

Sign the token on your server#

Keep the account id, key id and secret in server configuration (environment variables, a secrets manager, wp-config.php).

// npm install jsonwebtoken
import jwt from 'jsonwebtoken';

export function gydrIdentityToken(user) {
  return jwt.sign(
    {
      sub: String(user.id),
      aud: process.env.GYDR_ACCOUNT_ID,
      name: user.name,
      email: user.email,
      email_verified: user.emailVerified === true,
    },
    process.env.GYDR_IDENTITY_SECRET,
    {
      algorithm: 'HS256',
      expiresIn: '1h', // sets exp; iat is added automatically
      header: { kid: process.env.GYDR_IDENTITY_KID },
    },
  );
}

Give the widget a token endpoint (recommended)#

Add one URL on your own site that returns the current visitor’s token, and point the widget at it with data-identity-endpoint. The widget calls it itself — with the visitor’s cookies, never cached — before the first message and again whenever the token is under a minute from expiring.

<script
  src="https://cdn.gydr.ai/widget.js"
  data-api-key="pk_live_YOUR_KEY"
  data-identity-endpoint="/gydr/identity-token"
  async
></script>
  • The endpoint must be on the same site (same origin) as the page. A path like /gydr/identity-token is simplest.
  • Return { "token": null } for a guest. An error status or a network failure changes nothing — the widget keeps what it has and tries again shortly, so a blip never signs anyone out.
  • Never let a page cache or CDN store this response — it belongs to one person.

Or supply the token from JavaScript#

For a single-page app or a site where a plain endpoint doesn’t fit, register a function with Gydr.setIdentityTokenProvider(). Register it from the script tag’s load event so it is in place before the first message. Return the token, or null when nobody is signed in. Throw on errors instead of returning null — null means “signed out” and clears the conversation.

const script = document.createElement('script');
script.src = 'https://cdn.gydr.ai/widget.js';
script.dataset.apiKey = 'pk_live_YOUR_KEY';
script.async = true;
script.addEventListener('load', () => {
  window.Gydr.setIdentityTokenProvider(async () => {
    const res = await fetch('/api/gydr-token', { credentials: 'same-origin', cache: 'no-store' });
    if (!res.ok) throw new Error('token unavailable'); // keeps the current state
    const { token } = await res.json();
    return token; // a string, or null for a guest
  });
});
document.head.appendChild(script);

A site with no way to refresh can pass a fixed token with Gydr.identify({ identityToken }) or the data-identity-token attribute. It stops working when it expires, so prefer the endpoint. All options are listed in the JavaScript API and Data Attributes references.

Sign-out is automatic#

You don’t need to tell the widget when someone signs out. It starts a fresh visitor and a new, empty conversation when:

  • your endpoint or provider returns null;
  • a token was active and now none is supplied;
  • the token names a different person (a different sub).

A conversation that began signed in continues only for that same person — if someone else signs in on the same browser, it is cleared. A signed-in person’s past chats are shown only while they are signed in, and conversations that showed an order are never listed in past chats. Gydr.reset() still clears everything on demand.

Test a token in the Console#

Console → Settings → Identity verification has a tester. Paste a token your server produced; it tells you whether Gydr accepts it and, if not, why (see Troubleshooting). Then open your site signed in, ask the chatbot something personal (“where is my order?”), and ask again signed out.

That is the whole setup

An identity key, the signing code, the endpoint and one attribute on the script tag. The rest of this page — key rotation and troubleshooting — is reference for later.

Rotate a key without signing anyone out#

An account can have two active keys at once, so you can switch without a gap:

  1. Create a second key in the Console.
  2. Deploy its kid and secret to your server; new tokens are signed with it.
  3. Once the old tokens have expired (1 hour if you follow the recommendation), retire the first key.

A lost secret can’t be shown again — create a new key and retire the old one. If you think a secret leaked, rotate immediately.

Troubleshooting#

Each reason the Console tester reports, and the fix:

Tester says
bad_signature
Why
The token was signed with a different secret, or changed after signing.
Fix
Use the secret for the key named in kid; check nothing trims or re-encodes the token.
Tester says
expired
Why
exp is in the past (beyond the 60-second allowance).
Fix
Sign a fresh token for each request to your endpoint; check your server clock.
Tester says
wrong_audience
Why
aud is not your Gydr account id.
Fix
Copy the account id from Settings → Identity verification.
Tester says
unknown_key
Why
kid is missing, mistyped, or the key was retired.
Fix
Put the active key's id in the header's kid.
Tester says
too_many_claims
Why
More than 50 claims.
Fix
Send only what a connected plugin needs.
Tester says
lifetime_too_long
Why
exp is more than 24 hours after iat.
Fix
Use a lifetime of 1 hour.
Tester says
missing_subject
Why
No sub, or it is empty or over 255 characters.
Fix
Set sub to the user's id as text.
Tester says
unsupported_algorithm
Why
alg is not HS256 (for example RS256 or none).
Fix
Sign with HS256.
Tester says
invalid_claims
Why
A claim is a list or nested object, or longer than 256 characters.
Fix
Use plain values; put extra ids in attrs as text.
Tester says
too_large
Why
The token is over 8 KB.
Fix
Remove claims no plugin uses.
Tester says
not_yet_valid
Why
iat (or nbf) is more than 60 seconds in the future.
Fix
Fix your server clock (use NTP).
Tester says
forbidden_header
Why
The header carries crit, jku, jwk, x5u or x5c.
Fix
Send only alg, typ and kid in the header.
Tester says
malformed
Why
Not three base64url parts, not JSON, or iat/exp missing or not whole seconds.
Fix
Use a JWT library, or copy the PHP example exactly.

Next: Guests & data never to send · WordPress & WooCommerce setup

Ready to get started?

Create a free account and deploy your first chatbot in minutes.