import bcrypt from 'bcryptjs'; import jwt from 'jsonwebtoken'; import crypto from 'crypto'; import { Pool } from 'mysql2/promise'; import logger from '../utils/logger'; import { UserService } from '../services'; import { User, UserSession, CreateUserSessionDTO, UpdateUser, UserWithSession } from '../types/user'; export class AuthService { private userService: UserService; constructor(pool: Pool) { this.userService = new UserService(pool); } // Generate JWT token generateAccessToken(user: Partial) { return jwt.sign( { userId: user.id, username: user.username, role: user.role }, process.env.JWT_SECRET as string, { expiresIn: '15m' } ); }; // Generate JWT tokens generateTokens(user: Partial) { const accessToken = jwt.sign( { userId: user.id, username: user.username, role: user.role }, process.env.JWT_SECRET as string, { expiresIn: '15m' } ); const refreshToken = jwt.sign( { userId: user.id, username: user.username, role: user.role, type: 'refresh' }, // Include a claim to distinguish token types process.env.JWT_SECRET as string, { expiresIn: '7d' } // Longer expiry for refresh token ); const sessionToken = crypto.randomBytes(40).toString('hex'); return { accessToken, refreshToken, sessionToken }; } // Validate the user by username and password async validateUser(username: string, password: string): Promise { const user = await this.userService.getUserByUsername(username); if (!user) { throw new Error('Invalid credentials'); // Throw custom error } const isValid = await bcrypt.compare(password, user.password_hash); if (!isValid) { throw new Error('Invalid credentials'); // Throw custom error } return user; // Return the valid user }; async getUserById (id: number): Promise { return await this.userService.getUserById(id); }; async createUserSession (userSessionData: Partial) { const requiredFields = [ 'user_id', 'session_token', 'refresh_token', 'ip_address', 'user_agent', 'expires_at' ]; for (const field of requiredFields) { // Check for undefined or null only (allow empty strings) if (userSessionData[field as keyof UserSession] == null) { throw new Error(`Missing required field: ${field}`); } } logger.info('Creating new user session', { userSessionData }); const userSession = await this.userService.createUserSession(userSessionData); }; async updateUser (userId: number, user: UpdateUser) { this.userService.updateUser(userId, user); }; async getUserSessionByIp (userId: number, ipAdress: string): Promise { return await this.userService.getUserSessionByIp(userId, ipAdress); }; async getUserSessionByUserAndAgent (userId: number, userAgent: string): Promise { return await this.userService.getUserSessionByUserAndAgent(userId, userAgent); }; async getUserAndSessionByRefreshToken (refreshToken: string): Promise { return this.userService.getUserAndSessionByRefreshToken(refreshToken); }; async deleteUserSession (refreshToken: string) { this.userService.deleteUserSession(refreshToken); }; async updateUserSession (refreshToken:string, userSessionData: Partial) { const requiredFields = [ 'session_token', 'refresh_token', 'expires_at' ]; for (const field of requiredFields) { // Check for undefined or null only (allow empty strings) if (userSessionData[field as keyof UserSession] == null) { throw new Error(`Missing required field: ${field}`); } } logger.info('Updating user session', { userSessionData }); const userSession = await this.userService.updateUserSession(refreshToken, userSessionData); }; }