Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@katalysttech/auth

katalysttech140MIT1.0.41TypeScript support: included

A flexible authentication module for NestJS applications with JWT and refresh token support

nestjs, authentication, jwt, refresh-token, auth, typescript, katalysttech

readme

@katalyst/auth

A flexible and feature-rich authentication module for NestJS applications with support for JWT tokens, refresh tokens, multiple sessions, and various storage options.

cd existing_repo git remote add origin https://ntgitlab.novatechset.com/katalysttech-eco-system/auth-package.git git branch -M main git push -uf origin main

Features

  • 🔐 JWT-based authentication
  • 🔄 Refresh token support
  • 📱 Multiple device sessions
  • 💾 TypeORM and Redis storage support
  • 🔒 Password hashing utilities
  • 👥 Session management
  • 🛡️ Route protection
  • ⚙️ Highly configurable

Installation

npm install @katalyst/auth

Dependencies

Based on your storage choice, install the required dependencies:

For TypeORM storage:

npm install typeorm @nestjs/typeorm

For Redis storage:

npm install ioredis

Configuration

1. Module Setup

Synchronous Configuration

import { Module } from '@nestjs/common';
import { AuthModule } from '@katalyst/auth';
import { TypeOrmModule } from '@nestjs/typeorm';
import { RefreshToken } from './entities/refresh-token.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'your_username',
      password: 'your_password',
      database: 'your_database',
      entities: [RefreshToken],
      synchronize: true, // set to false in production
    }),
    AuthModule.register({
      jwt: {
        secret: process.env.JWT_SECRET,
        expiresIn: '15m',
      },
      refreshToken: {
        secret: process.env.REFRESH_TOKEN_SECRET,
        expiresIn: '7d',
        maxActiveSessions: 5, // Optional: limit active sessions per user
        storage: {
          type: 'typeorm',
          entity: RefreshToken,
        }
      },
      findUserById: async (id) => {
        // Implement your user lookup logic
        return await userRepository.findOne({ where: { id } });
      },
      findUserByUsername: async (username) => {
        // Implement your user lookup logic
        return await userRepository.findOne({ where: { username } });
      },
      userFields: {
        idField: 'id',
        usernameField: 'username',
        passwordField: 'password',
        tokenFields: ['role'] // Additional fields to include in JWT payload
      }
    }),
  ],
})
export class AppModule {}

Async Configuration

import { Module } from '@nestjs/common';
import { AuthModule } from '@katalyst/auth';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot(),
    AuthModule.registerAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        jwt: {
          secret: configService.get('JWT_SECRET'),
          expiresIn: configService.get('JWT_EXPIRES_IN'),
        },
        refreshToken: {
          secret: configService.get('REFRESH_TOKEN_SECRET'),
          expiresIn: configService.get('REFRESH_TOKEN_EXPIRES_IN'),
          maxActiveSessions: configService.get('MAX_ACTIVE_SESSIONS'),
          storage: {
            type: 'typeorm',
            entity: RefreshToken,
          }
        },
        // ... other config
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

2. Entity Setup (For TypeORM)

import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn } from 'typeorm';

@Entity('refresh_tokens')
export class RefreshToken {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  userId: string;

  @Column()
  tokenId: string;

  @Column()
  expiresAt: Date;

  @Column({ default: false })
  isRevoked: boolean;

  @CreateDateColumn()
  createdAt: Date;

  @Column('json', { nullable: true })
  deviceInfo: any;
}

Important Notes About Entity Setup

  1. The id field must be of type string to match the package's requirements
  2. Use @PrimaryGeneratedColumn('uuid') for string IDs
  3. If you need to use numeric IDs, you'll need to convert them to strings in your entity

Example with Numeric ID (if required)

@Entity('refresh_tokens')
export class RefreshToken {
  @PrimaryGeneratedColumn('increment')
  @Transform(({ value }) => value.toString())
  id: string;

  // ... other fields
}

Troubleshooting

Common Issues

  1. ID Type Mismatch If you see the error: "The types of '(new refreshToken.storage.entity(...)).id' are incompatible between these types. Type 'number' is not assignable to type 'string'", you need to ensure your RefreshToken entity's id field is of type string. See the Entity Setup section above for solutions.

  2. Storage Configuration When using TypeORM storage, make sure to:

    • Pass the correct DataSource instance
    • Use the correct entity type
    • Configure the proper ID field type

Usage

1. Authentication Controller

import { Controller, Post, Body, Get, UseGuards, Request } from '@nestjs/common';
import { AuthService, AuthGuard } from '@katalyst/auth';

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('login')
  async login(@Body() credentials: { username: string; password: string }) {
    return await this.authService.login(credentials.username, credentials.password);
  }

  @Post('refresh')
  async refresh(@Body() body: { refreshToken: string }) {
    return await this.authService.refreshToken(body.refreshToken);
  }

  @Get('sessions')
  @UseGuards(AuthGuard)
  async getSessions(@Request() req: any) {
    return await this.authService.getActiveSessions(req.user.sub);
  }

  @Post('logout')
  @UseGuards(AuthGuard)
  async logout(@Request() req: any) {
    await this.authService.logout(req.user.sub, req.user.tokenId);
    return { message: 'Logged out successfully' };
  }

  @Post('logout-all')
  @UseGuards(AuthGuard)
  async logoutAll(@Request() req: any) {
    await this.authService.logoutAll(req.user.sub);
    return { message: 'Logged out from all devices' };
  }
}

2. Protecting Routes

import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@katalyst/auth';

@Controller('protected')
@UseGuards(AuthGuard)
export class ProtectedController {
  @Get()
  getProtectedData() {
    return { message: 'This is protected data' };
  }
}

3. Password Utilities

import { Injectable } from '@nestjs/common';
import { PasswordService } from '@katalyst/auth';

@Injectable()
export class UserService {
  constructor(private readonly passwordService: PasswordService) {}

  async createUser(username: string, password: string) {
    const hashedPassword = await this.passwordService.hashPassword(password);
    // Save user with hashed password
  }

  async validatePassword(plainPassword: string, hashedPassword: string) {
    return await this.passwordService.comparePassword(plainPassword, hashedPassword);
  }
}

4. Session Management

import { Controller, Get, Post, UseGuards, Request, Param } from '@nestjs/common';
import { AuthService, AuthGuard } from '@katalyst/auth';

@Controller('sessions')
@UseGuards(AuthGuard)
export class SessionController {
  constructor(private readonly authService: AuthService) {}

  @Get()
  async getActiveSessions(@Request() req: any) {
    return await this.authService.getActiveSessions(req.user.sub);
  }

  @Post('revoke/:tokenId')
  async revokeSession(@Request() req: any, @Param('tokenId') tokenId: string) {
    await this.authService.revokeToken(req.user.sub, tokenId);
    return { message: 'Session revoked successfully' };
  }
}

Environment Variables

JWT_SECRET=your_jwt_secret_key
JWT_EXPIRES_IN=15m
REFRESH_TOKEN_SECRET=your_refresh_token_secret
REFRESH_TOKEN_EXPIRES_IN=7d
MAX_ACTIVE_SESSIONS=5

API Response Types

Login Response

interface AuthTokens {
  accessToken: string;
  refreshToken: string;
  expiresIn: number;
}

Active Session Response

interface Session {
  id: string;
  createdAt: Date;
  expiresAt: Date;
  deviceInfo?: {
    userAgent?: string;
    ip?: string;
    // ... other device info
  };
}

Error Handling

The module throws standard NestJS exceptions:

  • UnauthorizedException: Invalid credentials, invalid token, or revoked token
  • BadRequestException: Invalid refresh token or missing required fields
  • ForbiddenException: Token revoked or session limit exceeded

Best Practices

  1. Security

    • Store secrets in environment variables
    • Use strong secret keys
    • Set appropriate token expiration times
    • Implement rate limiting for auth endpoints
  2. Token Storage

    • Use secure storage for refresh tokens
    • Implement regular token cleanup
    • Monitor active sessions
  3. Error Handling

    • Implement proper error handling in your controllers
    • Log authentication failures
    • Provide clear error messages to users

Contributing

Contributions are welcome! Please read our contributing guidelines for details.

License

This project is licensed under the MIT License - see the LICENSE file for details.