123 lines
3.9 KiB
TypeScript
123 lines
3.9 KiB
TypeScript
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<User>) {
|
|
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<User>) {
|
|
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<User> {
|
|
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<User | null> {
|
|
return await this.userService.getUserById(id);
|
|
};
|
|
|
|
async createUserSession (userSessionData: Partial<UserSession>) {
|
|
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<UserSession | null> {
|
|
return await this.userService.getUserSessionByIp(userId, ipAdress);
|
|
};
|
|
async getUserSessionByUserAndAgent (userId: number, userAgent: string): Promise<UserSession | null> {
|
|
return await this.userService.getUserSessionByUserAndAgent(userId, userAgent);
|
|
};
|
|
|
|
async getUserAndSessionByRefreshToken (refreshToken: string): Promise<User | null> {
|
|
return this.userService.getUserAndSessionByRefreshToken(refreshToken);
|
|
};
|
|
|
|
async deleteUserSession (refreshToken: string) {
|
|
this.userService.deleteUserSession(refreshToken);
|
|
};
|
|
|
|
async updateUserSession (refreshToken:string, userSessionData: Partial<UserSession>) {
|
|
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);
|
|
};
|
|
|
|
}
|