Skip to content
ShopAssist Docs
Orders and identity

Identity JWT for Generic stores

Mint a short-lived HS256 identity token on your server for signed-in customers and pass it to the widget, with Python, Node.js and PHP examples.

Applies to: GenericView as Markdown

On a Generic store, your server tells ShopAssist who is signed in by minting a short-lived JWT. The widget asks your page for it when the shopper opens the chat.

Get the identity secret

In Settings → Connection → Signed-in customers, click Create identity secret. The secret is shown once, base64-encoded, with its key id (k_…). Store both in your server's secret store. The HMAC key is the decoded bytes of the secret, not the base64 text.

Token claims

ClaimValue
Header alg / typ / kidHS256 / JWT / your key id
issgeneric:sto_… (your store id)
audshopassist
subYour customer id (string, up to 128 characters)
sidThe widget session id passed to your identify callback
iat, expNow, and at most 10 minutes later (the examples use 5)
jtiAt least 128 random bits
email, name (optional)Shown to your team on handoff only; never used to find orders

sid is the widget session id that the widget passes to the identify callback.

Server side

# Python (PyJWT)
import base64, secrets, time, jwt
def mint_shopassist_token(store_id: str, key_id: str, secret_b64: str, customer_id: str, session_id: str) -> str:
    now = int(time.time())
    claims = {"iss": f"generic:{store_id}", "aud": "shopassist", "sub": str(customer_id), "sid": session_id,
              "iat": now, "exp": now + 300, "jti": secrets.token_hex(16)}
    return jwt.encode(claims, base64.b64decode(secret_b64), algorithm="HS256", headers={"kid": key_id})

Expose it as an endpoint on your site, for example POST /my-server/shopassist-token. The endpoint MUST be POST, send Cache-Control: no-store, authenticate the shopper with your own session, and return null for guests.

Browser side

ShopAssist.identify(async ({ sessionId }) => {
  const r = await fetch("/my-server/shopassist-token", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ session_id: sessionId }),
  })
  return r.ok ? ((await r.json()).token ?? null) : null
})

Call ShopAssist.identify(null) when the customer signs out. The provider is called with a timeout of 8 seconds; return null for guests.

Verification errors

If a token is rejected, the widget continues as a guest and the dashboard shows the reason (identity_invalid):

details.reason values of identity_invalid
ReasonMeaning
malformedThe token is not a valid JWT.
algorithmThe token is not signed with HS256.
unknown_kidThe `kid` header does not match the store's current or previous identity key.
signatureThe signature does not verify with the identity secret.
issuer`iss` is not `generic:{store_id}` (or the platform issuer).
audience`aud` is not `shopassist`.
session_mismatch`sid` is not the widget session that requested the token.
expiredThe token has expired.
not_yet_valid`iat` or `nbf` lies in the future (check the server clock).
lifetime_too_long`exp − iat` is longer than 300 seconds.

Never put the token in your page HTML or local storage, and never mint it in the browser: anyone with the secret can sign in as any customer. If the secret leaks, rotate it immediately.

Last updated on

On this page