Ferndesk
Help Center Authentication

JWT Authentication

Identify users in your help widget without requiring a separate login. When users are logged into your app, you can pass their identity to Ferndesk using a JWT token signed by your backend.

You'll need your JWT secret from Help Center > Customize > Access Control and the Ferndesk SDK installed.

How It Works

Three-step flow:

  1. Your frontend detects a logged-in user

  2. Your backend generates a signed JWT with user details

  3. Your frontend calls Ferndesk('identify', { jwt })

The help center and widget now knows who the user is for authentication, personalization and analytics.

The identify method only works from the same domain as your help center or a 1-level subdomain. If your help center is at help.example.com, you can identify from app.example.com but not otherdomain.com.

JWT Secret

Before you can sign JWTs, you need a signing secret from your Ferndesk dashboard:

  1. Go to Help Center, then under Manage, click Access Control.

  2. Expand the JWT identification row

  3. Click Generate Secret

You can only create one JWT secret per help center. If a secret already exists, you'll need to delete it before generating a new one.

The secret is shown only once when you generate it. Copy it immediately and store it securely in your backend environment variables. You won't be able to view it again.

If you lose your secret, you'll need to delete it and generate a new one. This will invalidate all existing JWT tokens signed with the old secret until you update your backend to use the new one.

Generate the JWT Server-Side

Create an endpoint that returns a signed token. Required claims:

  • sub (string, required): Unique ID in your system that identifies this user. This is the primary identity key.

  • email (string, required): User's email address

  • name (string, optional): Display name

  • customAttributes (object, optional): Extra metadata

  • exp (number, recommended): Token expiration timestamp

The sub claim must remain stable for each user. Ferndesk uses this subject to identify and link users. If a user's sub changes, they will not be linked to their previous help center identity.

Node.js example:

const jwt = require('jsonwebtoken');

app.get('/api/ferndesk-token', async (req, res) => {if (!req.user) return res.status(401).json({ error: 'Not authenticated' });

  const token = jwt.sign({
    sub: req.user.id,
    email: req.user.email,
    name: req.user.name,
    exp: Math.floor(Date.now() / 1000) + 3600 // 1 hour
  }, process.env.FERNDESK_JWT_SECRET, { algorithm: 'HS256' });

  res.send(token);
});

Python example:

import jwt
import time

@app.route('/api/ferndesk-token')
def ferndesk_token():
    if not current_user:
        return {'error': 'Not authenticated'}, 401

    token = jwt.encode({
        'sub': current_user.id,
        'email': current_user.email,
        'name': current_user.name,
        'exp': int(time.time()) + 3600
    }, os.environ['FERNDESK_JWT_SECRET'], algorithm='HS256')

    return token

Never expose your JWT secret in client-side code. Store it in environment variables server-side only.

Call Identify from Your Frontend

Fetch the token from your backend and pass it to the SDK:

Ferndesk('init', { widgetId: 'your-widget-id' });

fetch('/api/ferndesk-token').then(r => r.text())
  .then(jwt => Ferndesk('identify', { jwt }))
  .catch(err => console.error('Identification failed:', err));

React example:

useEffect(() => {
  window.Ferndesk('init', { widgetId: 'your-widget-id' });

  if (currentUser) {
    fetch('/api/ferndesk-token')
      .then(r => r.text())
      .then(jwt => window.Ferndesk('identify', { jwt }));
  }
}, [currentUser]);

Call identify after init but before opening the widget. To log out, reinitialize without calling identify.

Verify It's Working

Check these indicators:

  • Browser console: No errors. Invalid JWTs show Ferndesk: identify failed - invalid jwt

  • Contact form: Email and name will be pre-filled

  • Analytics: User sessions appear in your dashboard

Common Errors

Ferndesk: identify requires a jwt

Missing jwt parameter. Check that your backend is returning a JWT string.

invalid jwt

Signature verification failed. Verify:

  • Correct JWT secret matches what's stored in Ferndesk

  • Token hasn't expired

  • Algorithm is HS256

JWT subject does not match the existing help-center user

This error occurs when a user's email already exists in your help center but with a different subject ID. This means someone previously signed in with that email using a different identity system or sub value.

To resolve:

  • Ensure your backend always sends the same sub for each user

  • If you've changed user ID systems, the affected user will need to be re-provisioned in Ferndesk

must be called from same domain or 1-level subdomain

Domain mismatch. Your app and help center must share a root domain.

The sub claim is the primary identifier for users. While email is required, Ferndesk binds identity to the subject, not the email address alone. This prevents account takeover if email addresses change or are reused.

Security Notes

  • Set token expiration (1 hour is common)

  • Only generate tokens for authenticated users

  • Use HTTPS everywhere

  • Never commit secrets to version control

  • Keep your sub values stable—Ferndesk persists identity by subject

Was this helpful?