Skip to main content
The Node Forge Logo

The Node Forge presents:

JWT Utils

Lightweight JWT Authentication for Node.js

License: MIT

NPM Version Build Status

Live Documentation

A fast, lightweight Node.js JWT library for generating, verifying, and managing JSON Web Tokens (JWTs). Supports authentication and token-based authorization for APIs built with Express, Fastify, Koa, Hapi, NestJS, and Next.js. Ideal for securing web applications, handling user authentication, and implementing role-based access control (RBAC).


JWT Utils is a fast, lightweight, and framework-agnostic Node.js library for generating, verifying, and managing JSON Web Tokens (JWTs). It simplifies authentication and token-based authorization for web applications and APIs.

🚀 Features

  • Generate JWTs – Create signed JWTs with custom payloads and expiration times.
  • Verify JWTs – Securely decode and verify tokens.
  • Access & Refresh Tokens – Implement authentication with refresh token support.
  • Framework-Agnostic Middleware – Works with Express, Fastify, Koa, Hapi, NestJS, and Next.js.
  • RBAC Middleware – Control access based on user roles.
  • TypeScript Support – Fully typed for safer development.
  • Lightweight & Secure – Uses jsonwebtoken with best security practices.
  • ✅ Generate and verify JWTs easily
  • ✅ Support for Fastify, Koa, and Express middleware
  • ✅ Framework-agnostic core utilities
  • ✅ Lightweight and dependency-free
  • ✅ Built-in security best practices

📦 Installation

npm install @the-node-forge/jwt-utils

or

yarn add @the-node-forge/jwt-utils

🔧 Usage

Generating Access & Refresh Tokens

1⃣ Generate a Token (no options)

import { generateTokens } from '@the-node-forge/jwt-utils';

const accessSecret = 'your-access-secret';
const refreshSecret = 'your-refresh-secret';

const { accessToken, refreshToken } = generateTokens(
  { id: 'user123', role: 'admin' },
  accessSecret,
  refreshSecret,
);
console.log('Access Token:', accessToken);
console.log('Refresh Token:', refreshToken);
const token = generateTokens({ id: 'user123', role: 'admin' });

console.log(token);

** Generate a Token (custom options)**

const { accessToken, refreshToken } = generateTokens(
  { id: 'user123', role: 'admin' },
  accessSecret,
  refreshSecret,
  {
    accessExpiresIn: '1h', // Custom access token expiry
    refreshExpiresIn: '7d', // Custom refresh token expiry
    algorithm: 'HS512', // Stronger algorithm
    audience: 'my-app',
    issuer: 'my-auth-service',
  },
);

console.log('Access Token:', accessToken);
console.log('Refresh Token:', refreshToken);

Verifying Tokens

2⃣ Verify a Token

import { verifyToken, verifyRefreshToken } from '@the-node-forge/jwt-utils';

// no options
const decodedAccess = verifyToken(accessToken, accessSecret);
const decodedRefresh = verifyRefreshToken(refreshToken, refreshSecret);

// custom options
const decodedAccess = verifyToken(accessToken, accessSecret, {
  audience: 'my-app',
  issuer: 'auth-service',
});

const decodedRefresh = verifyRefreshToken(refreshToken, refreshSecret, {
  audience: 'my-app',
  issuer: 'auth-service',
});

console.log('Decoded Access Token:', decodedAccess);
console.log('Decoded Refresh Token:', decodedRefresh);

Verifying a Refresh Token

import { verifyRefreshToken } from '@the-node-forge/jwt-utils';

const refreshToken = 'your_refresh_jwt_token_here';
const refreshSecret = 'your-refresh-secret';

// no options
const decoded = verifyRefreshToken(refreshToken, refreshSecret);

// custom options
const decoded = verifyRefreshToken(refreshToken, refreshSecret, {
  audience: 'my-app',
  issuer: 'auth-service',
});

if (decoded) {
  console.log('Refresh token is valid:', decoded);
} else {
  console.log('Invalid or expired refresh token');
}

🚀 Integration with Web Frameworks

Express Middleware

import express from 'express';
import {
  authenticateToken,
  authenticateRefreshToken,
} from '@the-node-forge/jwt-utils/middleware/express';

const app = express();
app.use(express.json());

const ACCESS_SECRET = 'your-access-secret';
const REFRESH_SECRET = 'your-refresh-secret';

const user = {
  id: '123',
  role: 'admin',
};

// Generate tokens
app.post('/login', (req, res) => {
  const tokens = generateTokens(user, ACCESS_SECRET, REFRESH_SECRET);
  res.json(tokens);
});

// Protected route
app.get('/protected', authenticateToken(ACCESS_SECRET), (req, res) => {
  res.json({ message: 'Access granted', user: req.user });
});

// Refresh token route
app.post('/refresh', authenticateRefreshToken(REFRESH_SECRET), (req, res) => {
  const { exp, iat, ...userData } = req.user; // token returns exp, iat, id and role. You only want to pass in the users data for a refresh token

  const newTokens = generateTokens(userData, ACCESS_SECRET, REFRESH_SECRET);
  res.json(newTokens);
});

Fastify Middleware

import Fastify from 'fastify';
import {
  authenticateToken,
  authenticateRefreshToken,
} from '@the-node-forge/jwt-utils/middleware/fastify';
import { generateTokens } from '@the-node-forge/jwt-utils';

const app = Fastify();

const ACCESS_SECRET = 'your-access-secret';
const REFRESH_SECRET = 'your-refresh-secret';

const user = {
  id: '123',
  role: 'admin',
};

// Generate tokens
app.post('/login', async (req, reply) => {
  const tokens = generateTokens(user, ACCESS_SECRET, REFRESH_SECRET);
  reply.send(tokens);
});

// Protected route
app.get(
  '/protected',
  { preHandler: authenticateToken(ACCESS_SECRET) },
  async (req, reply) => {
    reply.send({ message: 'Access granted', user: req.user });
  },
);

// Refresh token route
app.post(
  '/refresh',
  { preHandler: authenticateRefreshToken(REFRESH_SECRET) },
  async (req, reply) => {
    const { exp, iat, ...userData } = req.user; // Strip exp & iat before regenerating tokens

    const newTokens = generateTokens(userData, ACCESS_SECRET, REFRESH_SECRET);
    reply.send(newTokens);
  },
);

Koa Middleware

import Koa from 'koa';
import Router from '@koa/router';
import bodyParser from 'koa-bodyparser';
import {
  authenticateToken,
  authenticateRefreshToken,
} from '@the-node-forge/jwt-utils/middleware/koa';
import { generateTokens } from '@the-node-forge/jwt-utils';

const app = new Koa();
const router = new Router();

const ACCESS_SECRET = 'your-access-secret';
const REFRESH_SECRET = 'your-refresh-secret';

const user = {
  id: '123',
  role: 'admin',
};

app.use(bodyParser()); // Parse JSON body

// Generate tokens
router.post('/login', async (ctx) => {
  const tokens = generateTokens(user, ACCESS_SECRET, REFRESH_SECRET);
  ctx.body = tokens;
});

// Protected route
router.get('/protected', authenticateToken(ACCESS_SECRET), async (ctx) => {
  ctx.body = { message: 'Access granted', user: ctx.state.user };
});

// Refresh token route
router.post('/refresh', authenticateRefreshToken(REFRESH_SECRET), async (ctx) => {
  const { exp, iat, ...userData } = ctx.state.user; // Strip exp & iat before regenerating tokens

  const newTokens = generateTokens(userData, ACCESS_SECRET, REFRESH_SECRET);
  ctx.body = newTokens;
});

Hapi Middleware

import Hapi from '@hapi/hapi';
import {
  authenticateToken,
  authenticateRefreshToken,
} from '@the-node-forge/jwt-utils/middleware/hapi';
import { generateTokens } from '@the-node-forge/jwt-utils';

const server = Hapi.server({
  port: 3000,
  host: 'localhost',
});

const ACCESS_SECRET = 'your-access-secret';
const REFRESH_SECRET = 'your-refresh-secret';

const user = {
  id: '123',
  role: 'admin',
};

// Generate tokens
server.route({
  method: 'POST',
  path: '/login',
  handler: (request, h) => {
    const tokens = generateTokens(user, ACCESS_SECRET, REFRESH_SECRET);
    return h.response(tokens).code(200);
  },
});

// Protected route
server.route({
  method: 'GET',
  path: '/protected',
  options: { pre: [{ method: authenticateToken(ACCESS_SECRET) }] },
  handler: (request, h) => {
    return h.response({ message: 'Access granted', user: request.app.user });
  },
});

// Refresh token route
server.route({
  method: 'POST',
  path: '/refresh',
  options: { pre: [{ method: authenticateRefreshToken(REFRESH_SECRET) }] },
  handler: (request, h) => {
    const { exp, iat, ...userData } = request.app.user; // Strip exp & iat before regenerating tokens

    const newTokens = generateTokens(userData, ACCESS_SECRET, REFRESH_SECRET);
    return h.response(newTokens).code(200);
  },
});

// Start server
const start = async () => {
  await server.start();
  console.log('Server running on http://localhost:3000');
};

start();

🛡 Role-Based Access Control (RBAC) using express

import express from 'express';
import { authenticateToken } from '@the-node-forge/jwt-utils/middleware/express';
import { authorizeRoles } from '@the-node-forge/jwt-utils/middleware/rbac';

const app = express();
const ACCESS_SECRET = 'your-access-secret';

// Admin route (requires authentication + admin role)
app.get(
  '/admin',
  authenticateToken(ACCESS_SECRET), // Ensure user is authenticated
  authorizeRoles('admin'), // Ensure user has the 'admin' role
  (req, res) => {
    res.json({ message: 'Welcome Admin', user: req.user });
  },
);

🔒 Security Best Practices

  • Always use secure, long, randomly generated secret keys in production.
  • Store tokens in HTTP-only cookies instead of local storage when possible.
  • Implement refresh tokens for long-term authentication.

🌍 License

This project is licensed under the MIT License.


🤝 Contributing

Want to suggest a feature? Open an issue or contribute!

We welcome contributions!

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature-name)
  3. Commit your changes (git commit -m 'Add feature')
  4. Push to the branch (git push origin feature-name)
  5. Open a pull request