import { PlatformType } from '@app/common/constants/platform-type.enum'; import { RoleType } from '@app/common/constants/role.type.enum'; import { UserEntity } from '@app/common/modules/user/entities'; import { BadRequestException, Injectable, UnauthorizedException, } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { JwtService } from '@nestjs/jwt'; import * as argon2 from 'argon2'; import { OAuth2Client } from 'google-auth-library'; import { UserSessionEntity } from '../../../../common/src/modules/session/entities'; import { UserSessionRepository } from '../../../../common/src/modules/session/repositories/session.repository'; import { UserRepository } from '../../../../common/src/modules/user/repositories'; import { HelperHashService } from '../../helper/services'; @Injectable() export class AuthService { private client: OAuth2Client; constructor( private jwtService: JwtService, private readonly userRepository: UserRepository, private readonly sessionRepository: UserSessionRepository, private readonly helperHashService: HelperHashService, private readonly configService: ConfigService, ) { this.client = new OAuth2Client(this.configService.get('GOOGLE_CLIENT_ID')); } async validateUser( email: string, pass: string, regionUuid?: string, platform?: PlatformType, ): Promise> { const user = await this.userRepository.findOne({ where: { email, region: regionUuid ? { uuid: regionUuid } : undefined, }, relations: ['roleType', 'project'], }); if (!user) { throw new BadRequestException('Invalid credentials'); } if ( platform === PlatformType.WEB && [RoleType.SPACE_OWNER, RoleType.SPACE_MEMBER].includes( user.roleType.type as RoleType, ) ) { throw new UnauthorizedException('Access denied for web platform'); } if (!user.isUserVerified) { throw new BadRequestException('User is not verified'); } if (!user.isActive) { throw new BadRequestException('User is not active'); } if (!user.hasAcceptedAppAgreement) { throw new BadRequestException('User has not accepted app agreement'); } const passwordMatch = await this.helperHashService.bcryptCompare( pass, user.password, ); if (!passwordMatch) { throw new BadRequestException('Invalid credentials'); } // eslint-disable-next-line @typescript-eslint/no-unused-vars // const { password, ...result } = user; delete user.password; return user; } async createSession(data): Promise { return await this.sessionRepository.save(data); } async getTokens( payload, isRefreshToken = true, accessTokenExpiry = '24h', refreshTokenExpiry = '30d', ) { const [accessToken, refreshToken] = await Promise.all([ this.jwtService.signAsync(payload, { secret: this.configService.get('JWT_SECRET'), expiresIn: accessTokenExpiry, }), isRefreshToken ? this.jwtService.signAsync(payload, { secret: this.configService.get('JWT_SECRET'), expiresIn: refreshTokenExpiry, }) : null, ]); return { accessToken, ...(isRefreshToken ? { refreshToken } : {}), }; } async login(user: any) { const payload = { email: user.email, userId: user.userId, uuid: user.uuid, sessionId: user.sessionId, role: user?.role, googleCode: user.googleCode, hasAcceptedWebAgreement: user.hasAcceptedWebAgreement, hasAcceptedAppAgreement: user.hasAcceptedAppAgreement, project: user?.project, bookingPoints: user?.bookingPoints, }; if (payload.googleCode) { const profile = await this.getProfile(payload.googleCode); user = await this.userRepository.findOne({ where: { email: profile.email }, }); if (!user) { return { profile }; } } const tokens = await this.getTokens(payload); await this.updateRefreshToken(user.uuid, tokens.refreshToken); return tokens; } async updateRefreshToken(userId: string, refreshToken: string) { const hashedRefreshToken = await this.hashData(refreshToken); await this.userRepository.update( { uuid: userId }, { refreshToken: hashedRefreshToken, }, ); } hashData(data: string) { return argon2.hash(data); } async getProfile(googleCode: string) { try { const ticket = await this.client.verifyIdToken({ idToken: googleCode, audience: this.configService.get('GOOGLE_CLIENT_ID'), }); const payload = ticket.getPayload(); return { ...payload, }; } catch (error) { throw new UnauthorizedException('Google login failed'); } } }