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, 'user@example.com', '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:
- Copy secret from Dashboard → Settings → Setup & Integration
- Update
LILA_JWT_SECRETin.env - 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
- PHP/Laravel JWT Guide - PHP examples
- Authentication Overview - Understanding JWT flow
- Widget Configuration - Widget setup