JWT Utils Function

JWT Utils Function provides a production-ready authentication utility based on access tokens and refresh tokens for Express-based applications.

It is designed to be:

  • Secure by default
  • Simple to integrate
  • Framework-agnostic
  • Compatible with MVC, feature-based, and clean architectures

Installation Guide

Install the component using the ServerCN CLI:

npx servercn add jwt-utils

What Is JWT?

A JSON Web Token (JWT) is a compact, URL-safe token used to securely transmit claims between a client and a server.

A JWT consists of three parts:

header.payload.signature

Describes the signing algorithm and token type:

{
  "alg": "HS256",
  "typ": "JWT"
}

Contains claims (data). In ServerCN, this typically includes the user identifier:

{
  "_id": "user_id_here",
  "iat": 1700000000,
  "exp": 1700000900
}

Ensures the token has not been tampered with. It is generated using your secret key.

Official docs

(Access vs Refresh) Token

ServerCN follows a dual-token strategy, which is the recommended approach for production systems.

  • Short-lived (e.g. 15m)
  • Sent with every authenticated request
  • Used for authorization
  • Stored in memory or HTTP-only cookies
  • Long-lived (e.g. 7d)
  • Used only to issue new access tokens
  • Should be stored securely (HTTP-only cookie or database)
  • Can be revoked or rotated

Token Flow Overview

1. User logs in: Server generates an access token and a refresh token

2. Client sends access token: Included in the Authorization header

3. Access token expires: Client sends refresh token to /auth/refresh

4. New access token issued: Session continues without re-login

Prerequisites

Ensure the following environment variables are defined before running the application:

JWT_ACCESS_SECRET = "your-access-secret";
JWT_REFRESH_SECRET = "your-refresh-secret";

JWT_ACCESS_SECRET: Used to sign and verify access tokens

JWT_REFRESH_SECRET: Used to sign and verify refresh tokens - These secrets must be different and kept private

Ensure the following configuration are defined:

src/configs/env.ts
interface Config {
  JWT_REFRESH_SECRET: string;
  JWT_ACCESS_SECRET: string;
}
 
const env: Config = {
  JWT_REFRESH_SECRET: process.env.JWT_ACCESS_SECRET!,
  JWT_ACCESS_SECRET: process.env.JWT_ACCESS_SECRET!
};
 
export default env;

Basic Implementation

src/utils/jwt.ts
import jwt from "jsonwebtoken";
import env from "../configs/env";
 
const ACCESS_TOKEN_EXPIRY = "15m";
const REFRESH_TOKEN_EXPIRY = "7d";
 
// Generate a short-lived access token
export function generateAccessToken(user: { _id: string }) {
  return jwt.sign({ _id: user._id }, process.env.JWT_ACCESS_SECRET!, {
    expiresIn: ACCESS_TOKEN_EXPIRY
  });
}
 
// Generate a long-lived refresh token
export function generateRefreshToken(userId: string) {
  return jwt.sign({ userId }, process.env.JWT_REFRESH_SECRET!, {
    expiresIn: REFRESH_TOKEN_EXPIRY
  });
}
 
// Verify and decode an access token
export function verifyAccessToken(token: string) {
  return jwt.verify(token, process.env.JWT_ACCESS_SECRET!) as {
    _id: string;
  };
}
 
// Verify and decode a refresh token
export function verifyRefreshToken(token: string) {
  return jwt.verify(token, process.env.JWT_REFRESH_SECRET!) as {
    userId: string;
  };
}
src/modules/auth/jwt.helpers.ts
import jwt from "jsonwebtoken";
import env from "../../shared/configs/env";
 
const ACCESS_TOKEN_EXPIRY = "15m";
const REFRESH_TOKEN_EXPIRY = "7d";
 
// Generate a short-lived access token
export function generateAccessToken(user: { _id: string }) {
  return jwt.sign({ _id: user._id }, process.env.JWT_ACCESS_SECRET!, {
    expiresIn: ACCESS_TOKEN_EXPIRY
  });
}
 
// Generate a long-lived refresh token
export function generateRefreshToken(userId: string) {
  return jwt.sign({ userId }, process.env.JWT_REFRESH_SECRET!, {
    expiresIn: REFRESH_TOKEN_EXPIRY
  });
}
 
// Verify and decode an access token
export function verifyAccessToken(token: string) {
  return jwt.verify(token, process.env.JWT_ACCESS_SECRET!) as {
    _id: string;
  };
}
 
// Verify and decode a refresh token
export function verifyRefreshToken(token: string) {
  return jwt.verify(token, process.env.JWT_REFRESH_SECRET!) as {
    userId: string;
  };
}

Usage Example

import {
  generateAccessToken,
  generateRefreshToken,
  verifyAccessToken
} from "../utils/jwt";
 
export async function issueTokens(userId: string) {
  // Short-lived token used for authorization
  const accessToken = generateAccessToken({ _id: userId });
 
  // Long-lived token used to refresh sessions
  const refreshToken = generateRefreshToken(userId);
 
  return { accessToken, refreshToken };
}
 
export async function decodeAccessToken(token?: string) {
  // If no token is provided, deny access immediately
  if (!token) {
    throw ApiError.unauthorized("Access token missing");
  }
 
  // jwt.verify throws if the token is invalid or expired
  const payload = verifyAccessToken(token);
 
  // Payload contains the user id
  return payload._id;
}

Best Practices

Avoid storing large objects or sensitive personal information (PII) in the JWT payload. The token is sent with every request, so keeping it small reduces bandwidth usage and improves performance.

  • Do: Store only essential identifiers (e.g., userId, role).
  • Don't: Store entire user profiles, preferences, or large arrays.
  • Access Tokens: Store in memory (for single-page apps) or HTTP-only cookies if possible to mitigate XSS attacks.
  • Refresh Tokens: Store in HTTP-only, Secure, SameSite cookies to prevent XSS and CSRF attacks.

Implement refresh token rotation to detect token theft. If a refresh token is reused, invalidate the entire token family for that user.

File & Folder Structure

Select a file to view its contents

Installation

npx servercn add jwt-utils