AdelieStack/src/lib/server/api/iam/reset-password-requests/reset-password-requests.service.ts

105 lines
3.8 KiB
TypeScript
Raw Normal View History

2025-01-05 05:13:36 +00:00
import type { ResetPasswordCodeDto } from '$lib/dtos/reset-password';
import type { ResetPasswordNewPasswordDto } from '$lib/dtos/reset-password/reset-password-new-password.dto';
import { inject, injectable } from '@needle-di/core';
import type { CreateLoginRequestDto } from '../../../../dtos/login/create-login-request.dto';
import type { } from '../../../../dtos/login/verify-login-request.dto';
import { LoggerService } from '../../common/services/logger.service';
import { VerificationCodesService } from '../../common/services/verification-codes.service';
import { BadRequest } from '../../common/utils/exceptions';
import { MailerService } from '../../mail/mailer.service';
import { ResetPasswordEmail } from '../../mail/templates/reset-password.template';
import { UsersRepository } from '../../users/users.repository';
import { UsersService } from '../../users/users.service';
import { SessionsService } from '../sessions/sessions.service';
import { ResetPasswordRequestsRepository } from './reset-password-requests.repository';
@injectable()
export class ResetPasswordRequestsService {
constructor(
private loggerService = inject(LoggerService),
private resetPasswordRequestsRepository = inject(ResetPasswordRequestsRepository),
private usersRepository = inject(UsersRepository),
private verificationCodesService = inject(VerificationCodesService),
private usersService = inject(UsersService),
private sessionsService = inject(SessionsService),
private mailer = inject(MailerService),
) {}
async verify({ email, code }: ResetPasswordCodeDto) {
// find the hashed verification code for the email
const resetPasswordRequest = await this.resetPasswordRequestsRepository.get(email);
// if no hashed code is found, the request is invalid
if (!resetPasswordRequest) throw BadRequest('Invalid code');
// verify the code
const isValid = await this.verificationCodesService.verify({
verificationCode: code,
hashedVerificationCode: resetPasswordRequest.hashedCode,
});
// if the code is invalid, throw an error
if (!isValid) {
throw BadRequest('Invalid code');
}
// burn the login request so it can't be used again
await this.resetPasswordRequestsRepository.delete(email);
// check if the user already exists
const existingUser = await this.usersRepository.findOneByEmail(email);
if (!existingUser) {
this.loggerService.log.debug('User not found for email', email);
throw BadRequest('Unable to reset password');
}
return true;
}
async sendResetPasswordCode({ email }: CreateLoginRequestDto) {
// check if the user already exists
const existingUser = await this.usersRepository.findOneByEmail(email);
if (!existingUser) {
this.loggerService.log.debug(`User not found for email: ${email}`);
return;
}
// remove any existing login requests
await this.resetPasswordRequestsRepository.delete(email);
// generate a new verification code and hash
const { verificationCode, hashedVerificationCode } = await this.verificationCodesService.generateCodeWithHash();
// create a new login request
await this.resetPasswordRequestsRepository.set({
email,
hashedCode: hashedVerificationCode,
});
// send the verification email
await this.mailer.send({
to: email,
template: new ResetPasswordEmail(verificationCode),
});
}
async resetPassword({ email, password, confirm_password }: ResetPasswordNewPasswordDto) {
if (password !== confirm_password) {
throw BadRequest('Passwords do not match');
}
// check if the user already exists
const existingUser = await this.usersRepository.findOneByEmail(email);
if (!existingUser) {
throw BadRequest('Unable to reset password');
}
await this.usersService.updatePassword(existingUser.id, password);
return true;
}
private async authExistingUser({ userId }: { userId: string }) {
return this.sessionsService.createSession(userId);
}
}