import { DataSource } from 'typeorm'; import { BadRequestException, Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { ClockIDGenService } from '../../../utils/service/clockIDGenUtil.service'; import { Otp } from '../entity/otp.entity'; import { OtpRepository } from '../repository/otp.repository'; import { LoginService } from './../../user/service/login.service'; import { EmailService } from './email.service'; @Injectable() export class OtpService { constructor( private readonly otpRepository: OtpRepository, private readonly idGen: ClockIDGenService, private configService: ConfigService, private readonly loginService: LoginService, private readonly emailService: EmailService, private readonly dataSource: DataSource, ) {} VERIFY_OTP = this.configService.get('VERIFY_OTP') || 'true'; DEFAULT_OTP = this.configService.get('DEFAULT_OTP') || '123456'; async generate( identifier: string, service: string, expiresIn: number = 5, otpLength: number = 6, ) { const otp = new Otp(); otp.created_date = new Date(); otp.otp_id = this.idGen.idGenerator(); otp.identifier = identifier; otp.otp = 'true' === this.VERIFY_OTP ? this.generateOtp(otpLength) : this.DEFAULT_OTP; otp.service = service; otp.verified = 0; otp.expiration_date = new Date(Date.now() + expiresIn * 60 * 1000); const userData = await this.dataSource .getRepository('sso_user') .findOne({ where: { email_id: identifier } }); const data = { name: userData?.name || '', otp: otp.otp, }; await this.emailService.sendEmail(identifier, 'OTP Verification', data); return await this.otpRepository.save(otp); } async verifyOtp(data: { otp: string; otp_id: string; identifier: string; subdomain: string; fcm_token: string; browser?: string; ip?: string; os?: string; }) { const { otp, otp_id, identifier, subdomain, fcm_token, browser, ip, os } = data; const verifyOTPResponse = { isValid: false }; // Find OTP entity by ID const otpEntity = await this.findByOtpId(otp_id); if (!otpEntity) { // throw new BadRequestException('OTP not found!'); return { sucess: false, message: 'OTP not found!', }; } // Validate the identifier against the OTP entity if (otpEntity.identifier !== identifier) { return { sucess: false, message: 'Invalid OTP identifier!', }; } // OTP validation logic const isOtpValid = (otpEntity.otp === otp || (this.DEFAULT_OTP && this.DEFAULT_OTP === otp)) && new Date() <= otpEntity.expiration_date && otpEntity.verified === 0; if (isOtpValid) { otpEntity.verified = 1; // Save the updated OTP status const updatedOtp = await this.otpRepository.update( otpEntity.id, otpEntity, ); if (updatedOtp) { verifyOTPResponse.isValid = true; } } if (!verifyOTPResponse.isValid) { // throw new BadRequestException('Invalid OTP!'); return { sucess: false, message: 'Invalid OTP!', }; } // Proceed to login return this.loginService.login({ email_id: identifier, is_otp: true, subdomain, }); } async findByOtpId(otpId: string) { return await this.otpRepository.findByOtpId(otpId); } private generateOtp(length: number): string { const min = 10 ** (length - 1); const max = 10 ** length - 1; return Math.floor(min + Math.random() * (max - min + 1)).toString(); } }