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-utilsWhat 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.signature1. Header
Describes the signing algorithm and token type:
{
"alg": "HS256",
"typ": "JWT"
}2. Payload
Contains claims (data). In ServerCN, this typically includes the user identifier:
{
"_id": "user_id_here",
"iat": 1700000000,
"exp": 1700000900
}3. Signature
Ensures the token has not been tampered with. It is generated using your secret key.
(Access vs Refresh) Token
ServerCN follows a dual-token strategy, which is the recommended approach for production systems.
1. Access Token
- Short-lived (e.g. 15m)
- Sent with every authenticated request
- Used for authorization
- Stored in memory or HTTP-only cookies
2. Refresh Token
- 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
1. MVC Structure
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;
};
}2. Feature Structure
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
Keep Payloads Small
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.
Secure Storage
- 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.
Token Rotation
Implement refresh token rotation to detect token theft. If a refresh token is reused, invalidate the entire token family for that user.