@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.


๐Ÿ“š Table of Contents


๐Ÿ”‘ License

This package requires a valid commercial license. A valid license key must be configured to use this package.

Visit https://rstechhub.gumroad.com to purchase a license.

โœจ 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+

๐Ÿ“ฆ Installation

npm install @rs-tech-hub/nestjs-refresh-token \
            @rs-tech-hub/nestjs-clock \
            @rs-tech-hub/nestjs-license-validator \
            @rs-tech-hub/nestjs-prisma \
            @rs-tech-hub/nestjs-service-operation \
            @rs-tech-hub/nestjs-user \
            uuid
yarn add @rs-tech-hub/nestjs-refresh-token \
         @rs-tech-hub/nestjs-clock \
         @rs-tech-hub/nestjs-license-validator \
         @rs-tech-hub/nestjs-prisma \
         @rs-tech-hub/nestjs-service-operation \
         @rs-tech-hub/nestjs-user \
         uuid
pnpm add @rs-tech-hub/nestjs-refresh-token \
         @rs-tech-hub/nestjs-clock \
         @rs-tech-hub/nestjs-license-validator \
         @rs-tech-hub/nestjs-prisma \
         @rs-tech-hub/nestjs-service-operation \
         @rs-tech-hub/nestjs-user \
         uuid

๐Ÿš€ 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 {}

๐Ÿ’ป Service Usage

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 Methods

createRefreshToken(userId: string, expiryDays?: number): Promise<string>

Create a new refresh token for a user.

Parameters:

  • userId - The ID of the user
  • expiryDays - 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 revoke
  • replacedByToken - 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 revoke
  • replacedByToken - 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
  revoked: boolean;        // Revocation status
  replacedByToken?: string; // Token that replaced this one
  expiresAt: Date;         // Expiration date
  createdAt: Date;         // Creation timestamp
  updatedAt: Date;         // Last update timestamp
}

โš ๏ธ Error Codes

Error CodeDescription
user-error:refresh-token-expiredToken has expired
user-error:refresh-token-invalidToken is invalid or not found
user-error:refresh-token-revokedToken has been revoked

๐Ÿ’ก Best Practices

  1. Token Rotation: Always revoke old tokens when issuing new ones
  2. Regular Cleanup: Run cleanupExpiredTokens() periodically (e.g., daily cron job)
  3. Expiry Duration: Use appropriate expiry times (7-30 days recommended)
  4. Revoke on Logout: Always revoke tokens when users log out
  5. Track Replacements: Use replacedByToken parameter to track token chains
  6. Validate Before Use: Always check token status before issuing new access tokens
  7. 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,
    };
  }
}

๐Ÿ“„ License

This package requires a valid commercial license. See LICENSE.txt for details. By using this software, you agree to the terms outlined in the Software License Agreement (SLA.md). The license grants you specific rights to use, modify, and deploy the software within the scope defined in the agreement. For full terms, conditions, and restrictions, please refer to the Software License Agreement.

๐Ÿ“‹ Release Notes

1.0.0

  • Initial release

1.0.1

  • Updates RefreshTokenService

๐Ÿ†˜ Support

For technical support and inquiries: