Feedjoltdocs
Developers

JWT SSO

Sign your logged-in users into the Feedjolt portal and widget with a short-lived JWT your backend mints. No second login. Configure claims, secrets, rotation.

If your app already knows who the user is, you shouldn't make them log in again to leave feedback. With JWT SSO your backend signs a short-lived token identifying the user; Feedjolt verifies it server-side and transparently signs them into the portal and the in-app widget.

How it works

  1. You configure a signing secret and algorithm at Dashboard -> Developers -> JWT SSO.
  2. When a signed-in user opens feedback, your backend mints a JWT with their claims, signed with that secret.
  3. You hand the token to Feedjolt - either on the widget via data-sso-token, or by POSTing it to the identify endpoint.
  4. Feedjolt verifies the signature, creates or updates the matching end user, and sets a server-side session cookie. No magic-link, no extra login.

The secret lives only on your server and on Feedjolt's. The browser only ever sees the resulting token - never the secret.

Configuration

JWT SSO is configured per workspace (owner only) at Dashboard -> Developers -> JWT SSO:

FieldNotes
secret_keyYour shared signing secret. 16-512 characters. Store it server-side only.
algorithmOne of HS256 (default), HS384, HS512. HMAC shared-secret signing.
issuerOptional. If set, the token's iss claim must match.
audienceOptional. If set, the token's aud claim must match.
sync_modeUPSERT (default) auto-creates unknown users; UPDATE_ONLY rejects tokens for users that don't exist yet.

Only HMAC algorithms are supported - there is no public-key (asymmetric) mode. Both sides hold the same secret.

Token claims

Mint a standard JWT signed with your secret. These claims are required - a token missing any of them is rejected:

ClaimMeaning
subYour stable user ID. Used as the end user's external ID.
emailThe user's email.
nameDisplay name.
iatIssued-at (standard JWT).
expExpiry (standard JWT). Keep it short - minutes, not days.

Optional claims enrich the synced profile:

ClaimMeaning
avatar_urlProfile image URL.
custom_fieldsObject of arbitrary key/values stored on the end user.
companyObject identifying the user's company - see below.

If you configured an issuer or audience, also include the matching iss / aud claims; they're only verified when configured.

Company claim

Pass company as an object to attach the user to a company record (created or updated on the fly). companies (an array) is also accepted - the first entry is used.

{
  "company": {
    "id": "org_123",
    "name": "Acme Inc",
    "plan": "enterprise",
    "mrr": 2400,
    "industry": "fintech",
    "employee_count": 120
  }
}

Only id and name are required for a company; the rest are optional.

Minting the token

Mint the JWT on your backend, where the secret is safe. Keep exp short - the token is single-use to bootstrap the session.

// Node.js - npm i jsonwebtoken
import jwt from "jsonwebtoken";

const token = jwt.sign(
  {
    sub: user.id,
    email: user.email,
    name: user.fullName,
    avatar_url: user.avatarUrl,
    company: { id: user.orgId, name: user.orgName },
  },
  process.env.FEEDJOLT_JWT_SECRET,
  { algorithm: "HS256", expiresIn: "5m" },
);
# Python - pip install pyjwt
import datetime, jwt

now = datetime.datetime.now(datetime.timezone.utc)
token = jwt.encode(
    {
        "sub": user.id,
        "email": user.email,
        "name": user.full_name,
        "avatar_url": user.avatar_url,
        "company": {"id": user.org_id, "name": user.org_name},
        "iat": now,
        "exp": now + datetime.timedelta(minutes=5),
    },
    FEEDJOLT_JWT_SECRET,
    algorithm="HS256",
)

Handing the token to Feedjolt

Widget: pass the token on the loader's data-sso-token attribute (see Widget configuration). The widget forwards it and the user is signed in inside the panel.

Portal / direct: POST the token to the identify endpoint for your workspace:

curl -X POST https://api.feedjolt.com/api/{workspace}/identify \
  -H "Content-Type: application/json" \
  -d '{"token": "YOUR_JWT_HERE"}'

On success Feedjolt returns the synced end user and sets a server-side session cookie (httpOnly, secure, SameSite=Lax). That cookie - not your JWT - keeps the user signed in afterward, so you mint a fresh short-lived JWT only at sign-in.

Key rotation

Rotating the signing secret is zero-downtime. When you save a new secret, Feedjolt keeps the previous one valid so tokens already in flight still verify. Up to the last 5 secrets stay accepted during rollover; older ones drop off automatically.

To rotate: set the new secret in the dashboard, deploy it to your backend, and let any in-flight tokens drain. No coordinated cutover needed.

Security notes

  • Verification is server-side. Feedjolt validates the signature, expiry, and (if set) issuer/audience on its own servers. Never trust identity asserted by the browser.
  • The secret never reaches the browser. Mint tokens on your backend only. Treat the secret like a password.
  • Keep exp short. The JWT only needs to live long enough to bootstrap the session cookie - minutes is plenty.
  • UPDATE_ONLY for closed systems. If you provision users out of band and never want SSO to create new ones, set sync_mode to UPDATE_ONLY.

On this page