All docs pages

Contract: user token

Version 2.

Purpose

When a user is signed in on your site, your backend signs a short token that says so. The messenger checks it with your public key and recognises the user without a second registration. A token has one of two uses:

  • The widget. A visitor signed in on your site writes to profiles without registering again (W-3, ID-1), and keeps their chats across browsers and devices, which a guest cannot (W-2).
  • The inbox. A profile owner signed in on your owner dashboard opens the inbox there and answers their visitors (I-1, contracts/inbox-host.md).

Parties

Party Does
Your backend Holds the private key and signs a token for the signed-in user
Your page Passes the token to the messenger: getUserToken on the element (contracts/widget-element.md), the loader's sign-in message (contracts/widget-host.md, version 3), or the inbox's sign-in message on your owner dashboard (contracts/inbox-host.md)
The messenger Verifies the token with the public key you uploaded, then starts a session for that user on your site: a visitor's in the widget, an owner's in the inbox

Your key

  • Generate an Ed25519 key pair (algorithm EdDSA) or a P-256 key pair (algorithm ES256). No other algorithm is accepted.
  • Upload the public key in the partner console, on the Integration page under User token keys (PC-2), as PEM (-----BEGIN PUBLIC KEY-----). The console shows its key id (kid).
  • Keep the private key on your server only. It never goes to a browser.
  • To rotate, upload the new public key first, sign with it, then remove the old one. Two keys can be active at once.

The token

A signed JWT (JWS compact form).

Part Field Value
Header alg EdDSA or ES256
Header kid The key id the console showed
Header typ JWT
Claims iss Your site's publishable key, pk_…
Claims aud The token's use: messenger-widget for a visitor in the widget or the element, messenger-inbox for a profile owner in the inbox. See "Audience"
Claims sub Your own stable id for this user. At most 128 characters. Never an email address, phone number or name. In an inbox token, the same id you send as the owner of the user's profiles (contracts/profile-sync.md, "The owner")
Claims iat When you signed it, in seconds since 1970
Claims exp When it stops being valid: at most 5 minutes after iat
Claims jti A random id for this token, 16 to 64 characters, never used twice
Claims name Optional, at most 60 characters. In a widget token, the name the profile owner sees for this visitor. In an inbox token, the name the inbox shows for the signed-in owner

Put nothing else personal in the token. The messenger does not need it, and it would travel through the user's browser.

Audience

aud says what the token is for, so a token signed for one use cannot be used for the other:

  • messenger-widget signs a visitor in to the widget or the element. The messenger refuses it in the inbox.
  • messenger-inbox signs a profile owner in to the inbox. The messenger refuses it in the widget and the element.

Sign each token for the page that asked for it. A visitor page asks for a widget token (contracts/widget-host.md, "Signing in"). Your owner dashboard asks for an inbox token (contracts/inbox-host.md, "Signing in").

Version 1 named one audience, messenger, for the widget. Until the first site goes live, the messenger still accepts messenger in the widget and the element, and refuses it in the inbox. From the day the first site goes live, it refuses messenger everywhere, so sign widget tokens with messenger-widget before you ask to go live.

What the messenger does with it

  1. It checks the signature with the key named by kid, and that alg matches that key.
  2. It checks iss is the site the widget or the inbox runs for, aud is the audience for that use (see "Audience"), exp has not passed and is at most 5 minutes after iat (allowing 60 seconds of clock difference), and jti was not seen before.
  3. It starts a session for this user on your site only, and the session reaches only your data (INV-10, ID-8).
    • In the widget, the visitor gets an id for your site that no other site will ever see (INV-5).
    • In the inbox, the owner session reaches the profiles on your site whose owner is this sub. The messenger looks them up on every request, so when you give a profile a new owner, the previous owner loses it at once (D-74). If no profile on your site has this owner, the inbox does not open (contracts/inbox-host.md, "No profile on this site").

If any check fails, the partner console shows the failure and its reason (expired, unknown key, bad signature, wrong site, wrong audience, replayed) under integration health, so you can fix it. In the widget, the visitor continues as a guest. In the inbox, the owner is not signed in, and the inbox says so. The token's text is never stored or logged.

Signing out

When the user signs out of your site, tell the messenger: getUserToken returns nothing, the loader sends sign-out (version 3 of contracts/widget-host.md), or your owner dashboard sends sign-out to the inbox (contracts/inbox-host.md). The messenger ends that user's session on that browser.

Security

  • Tokens are valid for at most 5 minutes and only once. Send them only over HTTPS, never in an address, and do not log them.
  • The messenger keeps only a fingerprint of each jti until its exp, to refuse a replay.
  • An inbox token opens the owner's conversations with their visitors. Sign one only for the user signed in to your owner dashboard, and only on a route served from the dashboard's own origin, which serves no public pages and no third-party scripts (contracts/inbox-host.md, "Signing in").
  • A leaked private key lets someone sign in as any of your users on your site, as a visitor or as a profile owner, and on no other site. Remove the key in the console at once, and upload a new one.

Versioning

New optional claims may be added without changing the ones above. A breaking change raises the version. Version 2 adds the inbox's audience and renames the widget's (see "Audience").