UNPKG

@katalysttech/auth

Version:

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

367 lines (294 loc) 9.25 kB
# @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.