Deskwerk· Workshop

KnowledgeMessaging widget

JWT login: the widget knows who's writing

Without authentication, every widget conversation is anonymous: the widget asks for a name, the history sticks to the browser, and the team has no idea whether “Max” is really Max. JWT login turns that around: the website tells the widget who’s logged in, cryptographically signed.

What changes

  • No more asking for details: name and email are already known, so the conversation starts right away.
  • One history across devices: the same customer sees her conversation on laptop and phone. The history is tied to the identity, not the browser.
  • Context you can trust: the ticket belongs to a real, identified customer profile. Automation and agents work with verified data instead of whatever someone typed into a form field.

The architecture in one picture

The widget never authenticates anything itself. It only passes along a token that your backend issues:

Browser (logged in)               Your backend                    Zendesk
       │  "give me a chat token"       │                             │
       ├──────────────────────────────►│  signs a JWT                │
       │◄──────────────────────────────┤  (Shared Secret)            │
       │  zE loginUser(jwt) ───────────┼────────────────────────────►│ verifies

In the frontend, that’s a callback that fetches the token, and it does so fresh every time. That way expired tokens get renewed automatically:

zE('messenger', 'loginUser', async (callback) => {
  const antwort = await fetch('/api/chat-token'); // your endpoint, logged-in users only
  const { jwt } = await antwort.json();
  callback(jwt);
});

// When the site logs out, log the chat out too:
zE('messenger', 'logoutUser');

The token itself is signed by your backend, using a signing key from the Admin Center and an identity from your system (a stable external ID, plus name and email). It’s short-lived: once exp passes, Zendesk rejects the call and the widget asks your callback again. That’s why you fetch dynamically instead of hard-wiring a token.

The security rules

  1. The signing key stays on the server. A secret in frontend code is a public secret. Then anyone can impersonate anyone.
  2. The token endpoint checks the session. Only someone logged into your site gets a token. The endpoint is part of your auth, not in front of it.
  3. Short lifetimes, not static tokens. Zendesk itself advises against static tokens; refreshing through the callback is the intended path.
  4. Keep the identity consistent: the same external ID as in your customer system. Otherwise you end up with duplicate profiles instead of one clean history.

One login, the whole support surface

The real payoff shows up when the same identity carries not just the widget, but the whole support surface. The Help Center has its own JWT path: single sign-on with its own secret, a signed token to the Zendesk SSO endpoint. Technically separate from the messaging token above, but fed from the same source: the login in your own system.

Two doors, one key. Whoever signs in with you once is known in the widget and logged into the Help Center, with no second login and no second password. For the customer, the line between knowledge base, “my requests,” and chat disappears; it feels like one place, not three systems.

That’s what opens up the interesting things: a logged-in Help Center can get personal, for example a “My requests” page that shows the real status of your own tickets. That’s worth a blueprint of its own.

Why there’s no live demo for this

A real JWT login needs a login system and a backend that signs tokens. On a static demo page, every bit of it would be faked. Instead of a mock-up, you get this blueprint. If you want to see it wired up live: this is the kind of hookup I build in client projects, token endpoint included.

← Back to: Messaging widget