JWT Tokens in TypeScript: Complete Guide with the jsonwebtoken Library
JSON Web Token (JWT) is an open standard (RFC 7519) for creating access tokens based on the JSON format. JWT is widely used for authentication and information exchange between a client and server. In TypeScript, thanks to strict typing, working with JWT becomes especially convenient and secure. In this article, we will look at how to use the popular jsonwebtoken library in TypeScript to create, verify, and manage JWT tokens.
Installation and Setup
To get started, install the necessary packages:
npm install jsonwebtokennpm install -D @types/jsonwebtokenThe first package is the library itself, the second is the TypeScript types for it. After installation, create a file jwt.service.ts where we will store all the token logic.
Creating a JWT Token
The main function of jsonwebtoken is sign(). Let's look at its usage with typing:
import jwt from 'jsonwebtoken';
interface TokenPayload { userId: number; email: string; role: 'user' | 'admin';}
const createAccessToken = (payload: TokenPayload): string => { const secret = process.env.JWT_SECRET || 'default_secret'; const options: jwt.SignOptions = { expiresIn: '15m', // token is valid for 15 minutes algorithm: 'HS256', };
return jwt.sign(payload, secret, options);};Important: never store the secret key in code. Use environment variables (.env file).
Token Verification and Decoding
To verify the authenticity of a token, the verify() function is used. It automatically checks the signature and expiration:
const verifyToken = (token: string): TokenPayload => { const secret = process.env.JWT_SECRET || 'default_secret';
try { const decoded = jwt.verify(token, secret) as TokenPayload; return decoded; } catch (error) { if (error instanceof jwt.TokenExpiredError) { throw new Error('Token expired'); } if (error instanceof jwt.JsonWebTokenError) { throw new Error('Invalid token'); } throw error; }};Note the type casting as TokenPayload — this ensures TypeScript knows the structure of the decoded data.
Refresh Tokens: Extending the Session
In practice, access tokens have a short lifespan (15-30 minutes), so a mechanism for refreshing them is needed. For this, a refresh token with a longer lifespan is used:
const createRefreshToken = (payload: TokenPayload): string => { const secret = process.env.JWT_REFRESH_SECRET || 'refresh_secret'; const options: jwt.SignOptions = { expiresIn: '7d', // one week algorithm: 'HS256', };
return jwt.sign(payload, secret, options);};
const refreshAccessToken = (refreshToken: string): string => { const secret = process.env.JWT_REFRESH_SECRET || 'refresh_secret';
try { const decoded = jwt.verify(refreshToken, secret) as TokenPayload; // Can check if the refresh token has been revoked in the database return createAccessToken({ userId: decoded.userId, email: decoded.email, role: decoded.role, }); } catch { throw new Error('Invalid refresh token'); }};Middleware for Express in TypeScript
Integrating JWT with Express.js requires creating middleware. Here is a typed version:
import { Request, Response, NextFunction } from 'express';
interface AuthRequest extends Request { user?: TokenPayload;}
const authMiddleware = (req: AuthRequest, res: Response, next: NextFunction) => { const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) { return res