How to Implement Refresh Token in NestJS Securely
In NestJS, implement refresh tokens by issuing a long-lived JWT refresh token alongside a short-lived access token using
@nestjs/jwt. Store the refresh token securely (e.g., HttpOnly cookie) and create an endpoint to verify and issue new access tokens when the refresh token is valid.Syntax
To implement refresh tokens in NestJS, you typically use the JwtModule to sign and verify tokens. You create two tokens: an access token with a short expiration and a refresh token with a longer expiration. The refresh token is stored securely and used to get new access tokens without re-authenticating the user.
sign(payload, { expiresIn }): creates a JWT token with expiration.verify(token): checks token validity.- Store refresh tokens securely, often in HttpOnly cookies.
- Create an endpoint to accept refresh tokens and return new access tokens.
typescript
const accessToken = this.jwtService.sign(payload, { expiresIn: '15m' }); const refreshToken = this.jwtService.sign(payload, { expiresIn: '7d' }); // To verify refresh token try { const decoded = this.jwtService.verify(refreshToken); // issue new access token } catch (e) { // handle invalid or expired token }
Example
This example shows a simple NestJS AuthService that issues access and refresh tokens, stores the refresh token in a cookie, and provides a refresh endpoint to get new access tokens.
typescript
import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { Response, Request } from 'express'; @Injectable() export class AuthService { constructor(private readonly jwtService: JwtService) {} login(user: any, res: Response) { const payload = { username: user.username, sub: user.userId }; const accessToken = this.jwtService.sign(payload, { expiresIn: '15m' }); const refreshToken = this.jwtService.sign(payload, { expiresIn: '7d' }); // Set refresh token in HttpOnly cookie res.cookie('refreshToken', refreshToken, { httpOnly: true, secure: true, // set to true in production with HTTPS sameSite: 'strict', maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days }); return { accessToken }; } refreshToken(req: Request, res: Response) { const token = req.cookies['refreshToken']; if (!token) { return null; // or throw unauthorized } try { const payload = this.jwtService.verify(token); const newAccessToken = this.jwtService.sign( { username: payload.username, sub: payload.sub }, { expiresIn: '15m' }, ); return { accessToken: newAccessToken }; } catch (e) { return null; // or throw unauthorized } } }
Output
{"accessToken":"<JWT_ACCESS_TOKEN>"}
Common Pitfalls
Common mistakes when implementing refresh tokens in NestJS include:
- Storing refresh tokens in localStorage or non-HttpOnly cookies, which exposes them to XSS attacks.
- Not verifying the refresh token before issuing a new access token.
- Not handling token expiration or invalid tokens properly, leading to security risks.
- Not rotating refresh tokens after use, which can increase risk if a token leaks.
typescript
/* Wrong: Storing refresh token in localStorage (vulnerable to XSS) */ localStorage.setItem('refreshToken', refreshToken); /* Right: Store refresh token in HttpOnly cookie (not accessible by JS) */ res.cookie('refreshToken', refreshToken, { httpOnly: true, secure: true });
Quick Reference
Tips for refresh token implementation in NestJS:
- Use
@nestjs/jwtfor token creation and verification. - Keep access tokens short-lived (e.g., 15 minutes).
- Keep refresh tokens long-lived (e.g., 7 days) and store securely in HttpOnly cookies.
- Verify refresh tokens on each request to refresh access tokens.
- Consider rotating refresh tokens after each use for better security.
Key Takeaways
Use short-lived access tokens and long-lived refresh tokens with @nestjs/jwt.
Store refresh tokens securely in HttpOnly cookies to prevent XSS attacks.
Always verify refresh tokens before issuing new access tokens.
Handle token expiration and invalid tokens gracefully to maintain security.
Consider rotating refresh tokens after each use to reduce risk of token theft.