@rs-tech-hub/nestjs-refresh-token
Refresh token management for NestJS applications. Create, validate, revoke, and manage JWT refresh tokens with automatic expiry and cleanup.
โจ Features
- ๐ Token Generation - Create unique refresh tokens with UUID
- โฐ Expiry Management - Configurable token expiration (default 7 days)
- ๐ซ Token Revocation - Revoke individual or all user tokens
- ๐งน Automatic Cleanup - Remove expired and revoked tokens
- ๐ Token Validation - Find and validate tokens by value
- ๐ค User Association - Link tokens to specific users
- ๐ Token Replacement - Track token replacement chains
๐ Prerequisites
- Node.js >= 18
- TypeScript >= 5.1.0
- NestJS >= 11.1.6
- Prisma ORM v7.0+
- PostgreSQL database
๐ Quick Start
Getting Started
This module is included in the NestJS Auth Bundle demo application. You can find the complete implementation at:
๐ GitHub: RuffSantiDev
The demo application includes all required dependencies and provides a working example of how to use this module.
Prisma Schema Setup
Include the Refresh Token Prisma schema in your project:
// In your main schema.prisma or import the schema
model RefreshToken {
id String @id @unique @default(cuid())
token String @unique
userId String
isRevoked Boolean @default(false)
revokedAt DateTime?
replacedByToken String?
expiresAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
}
Module Registration
Import the module in your NestJS application:
import { Module } from "@nestjs/common";
import { RefreshTokenModule } from "@rs-tech-hub/nestjs-refresh-token";
@Module({
imports: [RefreshTokenModule],
})
export class AppModule {}
๐ป Usage Examples
Creating Refresh Tokens
import { Injectable } from "@nestjs/common";
import { RefreshTokenService } from "@rs-tech-hub/nestjs-refresh-token";
@Injectable()
export class AuthService {
constructor(private refreshTokenService: RefreshTokenService) {}
async login(userId: string) {
// Create a refresh token (expires in 7 days by default)
const refreshToken =
await this.refreshTokenService.createRefreshToken(userId);
// Or specify custom expiry in days
const longLivedToken = await this.refreshTokenService.createRefreshToken(
userId,
30,
);
return {
accessToken: "jwt-access-token",
refreshToken,
};
}
}
Finding Tokens
// Find token by token value
const tokenData = await this.refreshTokenService.findByToken(tokenString);
// Find user ID by token
const userIdData =
await this.refreshTokenService.findUserIdByToken(tokenString);
// Find all active tokens for a user
const activeTokens =
await this.refreshTokenService.findActiveTokensForUser(userId);
Revoking Tokens
// Revoke a specific token by ID
await this.refreshTokenService.revokeToken(tokenId);
// Revoke token by token value
await this.refreshTokenService.revokeTokenByValue(tokenString);
// Revoke token and specify replacement
await this.refreshTokenService.revokeToken(oldTokenId, newTokenString);
// Revoke all tokens for a user
const result = await this.refreshTokenService.revokeAllUserTokens(userId);
Token Cleanup
// Clean up expired tokens (retention period: 60 days)
await this.refreshTokenService.cleanupExpiredTokens();
// Delete all expired and revoked tokens
const deletedCount =
await this.refreshTokenService.deleteExpiredAndRevokedTokens();
๐ง Repository Usage
The package also exports RefreshTokenRepository for direct database access:
import { Injectable } from "@nestjs/common";
import { RefreshTokenRepository } from "@rs-tech-hub/nestjs-refresh-token";
@Injectable()
export class TokenManagementService {
constructor(private refreshTokenRepo: RefreshTokenRepository) {}
async getTokensByUser(userId: string) {
return this.refreshTokenRepo.findMany({ userId });
}
async deleteToken(tokenId: string) {
return this.refreshTokenRepo.delete({ id: tokenId });
}
async countUserTokens(userId: string) {
return this.refreshTokenRepo.count({ userId });
}
}
๐ API Reference
RefreshTokenService
createRefreshToken(userId: string, expiryDays?: number): Promise<string>
Create a new refresh token for a user.
Parameters:
userId- The ID of the userexpiryDays- Token expiry in days (default: 7)
Returns: The token string (UUID v4)
findByToken(token: string): Promise<RefreshTokenServiceOutput>
Find a refresh token by its token value.
Returns: Token data including id, userId, expiresAt, revoked status
findUserIdByToken(token: string): Promise<{ userId: string }>
Find the user ID associated with a token.
Returns: Object containing userId
findActiveTokensForUser(userId: string): Promise<RefreshTokenServiceOutput[]>
Find all active (not revoked and not expired) tokens for a user.
Returns: Array of active refresh tokens
revokeToken(tokenId: string, replacedByToken?: string): Promise<RefreshTokenServiceOutput>
Revoke a refresh token by its ID.
Parameters:
tokenId- The ID of the token to revokereplacedByToken- Optional new token that replaces this one
Returns: The updated token
revokeTokenByValue(token: string, replacedByToken?: string): Promise<RefreshTokenServiceOutput>
Revoke a refresh token by its token value.
Parameters:
token- The token string to revokereplacedByToken- Optional new token that replaces this one
Returns: The updated token
revokeAllUserTokens(userId: string): Promise<ServiceResult<string, boolean>>
Revoke all tokens for a specific user.
Returns: ServiceResult indicating success or failure
cleanupExpiredTokens(): Promise<ServiceResult>
Delete expired and old revoked tokens (60 day retention period).
Returns: ServiceResult with deletion count
deleteExpiredAndRevokedTokens(): Promise<number>
Delete all expired and revoked tokens immediately.
Returns: Number of deleted tokens
๐ Data Model
RefreshTokenModel
{
id: string; // Unique token ID
token: string; // UUID v4 token value
userId: string; // Associated user ID
isRevoked: boolean; // Revocation status
revokedAt?: Date; // Revocation timestamp
replacedByToken?: string; // Token that replaced this one
expiresAt: Date; // Expiration date
createdAt: Date; // Creation timestamp
updatedAt: Date; // Last update timestamp
}
โ ๏ธ Error Codes
| Error Code | Description |
|---|---|
user-error:refresh-token-expired | Token has expired |
user-error:refresh-token-invalid | Token is invalid or not found |
user-error:refresh-token-revoked | Token has been revoked |
๐ก Best Practices
- Token Rotation: Always revoke old tokens when issuing new ones
- Regular Cleanup: Run
cleanupExpiredTokens()periodically (e.g., daily cron job) - Expiry Duration: Use appropriate expiry times (7-30 days recommended)
- Revoke on Logout: Always revoke tokens when users log out
- Track Replacements: Use
replacedByTokenparameter to track token chains - Validate Before Use: Always check token status before issuing new access tokens
- Limit Active Tokens: Consider limiting the number of active tokens per user
๐ Typical Token Refresh Flow
@Injectable()
export class AuthService {
constructor(
private refreshTokenService: RefreshTokenService,
private jwtService: JwtService,
) {}
async refreshAccessToken(refreshToken: string) {
// 1. Find the refresh token
const tokenData = await this.refreshTokenService.findByToken(refreshToken);
// 2. Validate token
if (!tokenData) {
throw new Error("user-error:refresh-token-invalid");
}
if (tokenData.revoked) {
throw new Error("user-error:refresh-token-revoked");
}
if (tokenData.expiresAt < new Date()) {
throw new Error("user-error:refresh-token-expired");
}
// 3. Generate new tokens
const accessToken = this.jwtService.sign({ sub: tokenData.userId });
const newRefreshToken = await this.refreshTokenService.createRefreshToken(
tokenData.userId,
);
// 4. Revoke old refresh token and link to new one
await this.refreshTokenService.revokeToken(tokenData.id, newRefreshToken);
return {
accessToken,
refreshToken: newRefreshToken,
};
}
}
๐ Release Notes
1.1.0
New Features
- Added RefreshTokenScheduler to automatically clean up expired tokens every 6 hours
- Cleanup runs automatically on application startup
- Enhanced cleanup with 60-day retention period for expired/revoked tokens
- Improved token management with better revocation tracking
1.0.1
- Updates RefreshTokenService methods
1.0.0
- Initial release
- Token generation with UUID v4
- Token validation and verification
- Token expiration (default 7 days)
- Token revocation support
- User token management
- Token replacement tracking
๐ License
This package requires a valid RS Tech Hub commercial license key. The software is protected by intellectual property laws and is provided under a commercial license agreement.
License Terms
- Commercial use requires a valid license key
- Development testing allowed with free trial
- Production deployment requires a paid license from https://rstechhub.gumroad.com
- Contact insights@rs-tech-hub.com for questions and inquiries
๐ Issues & Support
For issues, feature requests, or general questions contact insights@rs-tech-hub.com
Please review the docs first: https://rs-tech-hub.com/docs
๐ Website
Visit the website for more information: https://rs-tech-hub.com
Newsletter
Stay updated with the latest news and updates by subscribing to the newsletter at https://rs-tech-hub.kit.com/newsletter
Built with โค๏ธ by RS Tech Hub