JWT with JavaScript & Node.js

Generate JWT tokens in Node.js to authenticate users with LILA’s AI chatbot. Once authenticated, each user gets personalized responses based on their own data.

Prerequisites

  • Node.js 16+ or 18+
  • npm or yarn
  • Express.js (for server examples)

Installation

npm install jsonwebtoken
# or
yarn add jsonwebtoken

Node.js / Express Implementation

Create a JWT token for LILA with this basic function:

Basic JWT Generation

const jwt = require('jsonwebtoken');

function generateLilaToken(userId, userEmail, userName) {
  const jwtSecret = process.env.LILA_JWT_SECRET;

  const payload = {
    user_id: String(userId),      // Must be string
    email: userEmail,
    name: userName,
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + (60 * 60 * 2) // 2 hours
  };

  return jwt.sign(payload, jwtSecret, { algorithm: 'HS256' });
}

// Usage
const token = generateLilaToken(12345, '[email protected]', 'John Doe');
console.log(token);

With Custom Claims

function generateLilaTokenWithClaims(user, customClaims = {}) {
  const jwtSecret = process.env.LILA_JWT_SECRET;

  const payload = {
    user_id: String(user.id),
    email: user.email,
    name: user.name,
    role: user.role || 'customer',
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + (60 * 60 * 2),
    ...customClaims
  };

  return jwt.sign(payload, jwtSecret, { algorithm: 'HS256' });
}

// Usage with optional claims
const token = generateLilaTokenWithClaims(user, {
  role: 'admin',
  tenant_id: 'tenant_123'
});

Token Validation

function validateLilaToken(token) {
  const jwtSecret = process.env.LILA_JWT_SECRET;

  try {
    const decoded = jwt.verify(token, jwtSecret, { algorithms: ['HS256'] });
    return decoded;
  } catch (error) {
    if (error.name === 'TokenExpiredError') {
      return { error: 'Token expired' };
    }
    if (error.name === 'JsonWebTokenError') {
      return { error: 'Invalid token' };
    }
    return { error: 'Verification failed' };
  }
}

// Usage
const decoded = validateLilaToken(token);
if (!decoded.error) {
  console.log('Valid token for user:', decoded.user_id);
} else {
  console.error('Token validation failed:', decoded.error);
}

Express.js Integration

Service Module

Create services/lilaService.js:

const jwt = require('jsonwebtoken');

class LilaService {
  /**
   * Generate JWT token for user
   * @param {Object} user - User object
   * @param {Object} customClaims - Additional claims
   * @returns {string} JWT token
   */
  static generateToken(user, customClaims = {}) {
    if (!user || !user.id) {
      throw new Error('User object with id is required');
    }

    const payload = {
      user_id: String(user.id),
      email: user.email,
      name: user.name,
      iat: Math.floor(Date.now() / 1000),
      exp: Math.floor(Date.now() / 1000) + (60 * 60 * 2), // 2 hours
      ...customClaims
    };

    return jwt.sign(
      payload,
      process.env.LILA_JWT_SECRET,
      { algorithm: 'HS256' }
    );
  }

  /**
   * Get complete LILA configuration for widget
   * @param {Object} user - User object
   * @returns {Object} Configuration object
   */
  static getConfig(user) {
    if (!user) {
      return {};
    }

    return {
      api_key: process.env.LILA_API_KEY,
      jwt_token: this.generateToken(user)
    };
  }

  /**
   * Generate token with custom expiration
   * @param {Object} user - User object
   * @param {number} expiresInSeconds - Expiration time
   * @returns {string} JWT token
   */
  static generateTokenWithExpiry(user, expiresInSeconds) {
    const payload = {
      user_id: String(user.id),
      email: user.email,
      name: user.name,
      iat: Math.floor(Date.now() / 1000),
      exp: Math.floor(Date.now() / 1000) + expiresInSeconds
    };

    return jwt.sign(
      payload,
      process.env.LILA_JWT_SECRET,
      { algorithm: 'HS256' }
    );
  }

  /**
   * Validate JWT token
   * @param {string} token - JWT token
   * @returns {Object} Decoded payload or error
   */
  static validateToken(token) {
    try {
      return jwt.verify(token, process.env.LILA_JWT_SECRET, {
        algorithms: ['HS256']
      });
    } catch (error) {
      return { error: error.message };
    }
  }
}

module.exports = LilaService;

Environment Configuration

Create .env:

LILA_API_KEY=pk_live_abc123...
LILA_JWT_SECRET=your-32-char-secret-from-dashboard

Load in your app:

// app.js or server.js
require('dotenv').config();

API Token Refresh Endpoint

// routes/api.js
const express = require('express');
const router = express.Router();
const LilaService = require('../services/lilaService');
const authMiddleware = require('../middleware/auth');

// Token refresh endpoint
router.post('/lila/refresh-token', authMiddleware, (req, res) => {
  try {
    const token = LilaService.generateToken(req.user);

    res.json({
      token: token,
      expires_in: 7200 // 2 hours in seconds
    });
  } catch (error) {
    res.status(500).json({
      error: 'Failed to generate token',
      message: error.message
    });
  }
});

module.exports = router;

View Integration (EJS Example)

<!-- views/dashboard.ejs -->
<!DOCTYPE html>
<html>
<head>
  <title>Dashboard</title>
  <script type="module" src="https://embed.getlila.one/loader.js"></script>
</head>
<body>
  <h1>Dashboard</h1>

  <% if (lila && lila.jwt_token) { %>
    <lila-widget
      api-key="<%= lila.api_key %>"
      jwt-token="<%= lila.jwt_token %>">
    </lila-widget>
  <% } %>
</body>
</html>

Production Checklist

  • JWT secret stored in environment variables
  • Secret is 32+ characters, randomly generated
  • Token expiration set to 1-2 hours
  • HTTPS enabled in production
  • User ID cast to string in JWT payload
  • Error handling for failed token generation

Troubleshooting

Common JWT issues and solutions:

“Invalid signature” Error

Cause: JWT secret doesn’t match Dashboard Solution:

  1. Copy secret from Dashboard → Settings → Setup & Integration
  2. Update LILA_JWT_SECRET in .env
  3. Restart Node.js server

”user_id claim missing” Error

Cause: User ID not in JWT payload Solution: Ensure payload includes user_id as string:

user_id: String(user.id), // NOT just user.id

Token Expires Immediately

Cause: Server time incorrect or exp calculation wrong Solution:

// Correct calculation (seconds since epoch)
exp: Math.floor(Date.now() / 1000) + (60 * 60 * 2)

// WRONG (milliseconds)
exp: Date.now() + (60 * 60 * 2)

Next Steps