What JWT SSO is?
JWT SSO lets your users access your private HelpCenter.io help center – or use the embedded widget on your authenticated SaaS – without seeing a login screen on our side. Your application authenticates the user the way it already does. You then sign a short-lived JSON Web Token identifying who they are, and HelpCenter.io trusts that signed token.
There's no second password. No "Sign in with HelpCenter" button. No new account to provision in our dashboard.
What it works on?
Three surfaces share the same JWT contract. You configure SSO once and it covers all three:
Surface | What it looks like | How the JWT gets in |
|---|---|---|
Smart Widget | The floating help button you embed inside your SaaS app via | Host page passes the token via |
JS-only help center | The full help center rendered inside an iframe you embed on your own pages | Host page passes the token in the iframe URL: |
Direct help center | Visitors landing on your help center URL ( | The redirect-based flow described below — visitor is redirected to your login URL, your backend mints a JWT, redirects back to |
You don't pick one — all three are gated by the same shared secret and the same set of required claims.
The flow (direct help center)
A visitor lands on your private help center (e.g.
https://docs.example.com/article/foo).We see no session and look up your SSO configuration.
We redirect the visitor to your Login URL, passing along where they were trying to go.
Your backend authenticates the visitor (or recognizes them from an existing session).
Your backend mints a JWT identifying the visitor, signed with the shared secret you and we agreed on. HS256.
Your backend 302s the visitor back to
https://docs.example.com/sso/jwt?jwt=<token>.We verify the signature, validate the claims, log the visitor in, and redirect them to the URL they originally requested.
The flow (embedded widget)
Your host page authenticates the user the way it always has.
When the user lands on a page that embeds the widget, your backend mints a JWT for them (same shared secret, same claims).
The host snippet hands the JWT to the widget:
window.hcOptions = { jwt: '<token>', ... }.The widget includes
Authorization: Bearer <token>on every call to our embed API.We verify the signature on every request and apply your help center's visibility rules.
If the token expires while the widget is open, we return
403 SITE_AUTH_REQUIREDand the widget asks youronAuthExpiredcallback for a fresh token. No reload, no login screen.
What you're trusting us with
The shared secret. That's it. It signs every JWT you mint; we verify the signature against it. We never see your users' actual passwords; we never store them.
Required claims
Every JWT must include these claims. If any are missing, the token is rejected with jwt_missing_required_claim.
jti— unique token identifier. We use it for replay protection (the samejticannot be used twice on the same site).iss— issuer. Typically your application's domain.iat— issued at, Unix seconds.exp— expiry, Unix seconds. This is now required (was previously optional). Keep it short — 5 minutes is typical.email— the user's email.name— the user's display name.
Optional claims
external_id— your stable user ID. Strongly recommended. Lets us track the same user across email changes.lang— two-letter language code. Defaults to the site's default language.avatar_url— public URL to the user's avatar.role—viewer,editor, oradmin. Defaults toviewer.custom_fields— free-form object surfaced on the User record.aud— audience. Only required if you've set an Audience value in our dashboard.
What you control from the dashboard
Settings → Security → SSO is where this all lives. Self-serve, no support ticket needed (was Enterprise-only with a support handoff prior to v2.18 — that changed).
Login URL — where to send visitors to be authenticated.
Shared Secret — generate one or paste your own. Minimum 64 characters.
Logout URL — optional. Where to send visitors after logout.
Issuer — optional. If set, JWTs whose
issdoesn't match are rejected.Audience — optional. Same idea for
aud.Token TTL — hard cap on token age applied by the widget surface, even if
expis generous. Defaults to 300 seconds.
Plan availability
JWT SSO is available on our Catalyst, and Enterprise plans. No support ticket required to enable. You can open the Settings page and configure it.
Other SSO providers (SAML 2.0, OIDC, Google Workspace, Okta, Microsoft Entra ID) are on the roadmap. Our special placeholder cards in your dashboard let you signal demand for the one you want most.
Security notes
Tokens are single-use on the widget surface. A replayed
jtiis rejected. Mint a fresh token for each session your host page starts.The widget never stores the JWT in localStorage. It lives in memory only for the lifetime of the page.
When the host snippet passes the JWT to the widget iframe, it does so via the URL hash (
#jwt=...), not the query string. Hashes are not sent to the server — the token doesn't land in proxy or access logs.Clock skew tolerance is 30 seconds on both
iatandexp. Sync your servers via NTP.