npm package: @katalysttech/auth
Description: A flexible authentication module for NestJS applications with JWT and refresh token support
Author: KatalystTech
Latest version: 1.0.48

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

```bash
npm install @katalyst/auth
```

### Dependencies

Based on your storage choice, install the required dependencies:

**For TypeORM storage:**
```bash
npm install typeorm @nestjs/typeorm
```

**For Redis storage:**
```bash
npm install ioredis
```

## Configuration

### 1. Module Setup

#### Synchronous Configuration

```typescript
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

```typescript
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)

```typescript
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)
```typescript
@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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```env
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
```typescript
interface AuthTokens {
  accessToken: string;
  refreshToken: string;
  expiresIn: number;
}
```

### Active Session Response
```typescript
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.