Ferndesk
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', { token })

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.

Generate the JWT Server-Side

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

  • userId (string): Unique ID in your system

  • email (string): User's email

  • name (string, optional): Display name

  • customAttributes (object, optional): Extra metadata

  • exp (number, recommended): Token expiration timestamp

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({
    userId: 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({
        'userId': 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(token => Ferndesk('identify', { token }))
  .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(token => window.Ferndesk('identify', { token }));
  }
}, [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 tokens show Ferndesk: identify failed - invalid token

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

  • Analytics: User sessions appear in your dashboard

Common Errors

identify requires a token

Missing token parameter. Check your backend is returning a JWT string.

invalid token

Signature verification failed. Verify:

  • Correct JWT secret

  • Token hasn't expired

  • Algorithm is HS256

must be called from same domain or 1-level subdomain

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

Security Notes

  • Set token expiration (1 hour is common)

  • Only generate tokens for authenticated users

  • Use HTTPS everywhere

  • Never commit secrets to version control

Was this helpful?