From d3f9527f85c354a1a6d51bc8fc6463caaaeb947f Mon Sep 17 00:00:00 2001 From: Maxime Lalo Date: Fri, 24 Nov 2023 14:14:19 +0100 Subject: [PATCH] :sparkles: Refacto login --- src/app/api/customer/AuthController.ts | 76 +++++++------------ .../CustomersService/CustomersService.ts | 64 +++++++++++++++- 2 files changed, 90 insertions(+), 50 deletions(-) diff --git a/src/app/api/customer/AuthController.ts b/src/app/api/customer/AuthController.ts index bd88164a..4c9f000b 100644 --- a/src/app/api/customer/AuthController.ts +++ b/src/app/api/customer/AuthController.ts @@ -3,7 +3,13 @@ import { Controller, Post } from "@ControllerPattern/index"; import ApiController from "@Common/system/controller-pattern/ApiController"; import { Service } from "typedi"; import { EnrollmentResponse } from "@Services/common/Id360Service/Id360Service"; -import CustomersService, { SmsNotExpiredError } from "@Services/customer/CustomersService/CustomersService"; +import CustomersService, { + InvalidPasswordError, + InvalidTotpCodeError, + NotRegisteredCustomerError, + SmsNotExpiredError, + TotpCodeExpiredError, +} from "@Services/customer/CustomersService/CustomersService"; import AuthService, { ICustomerJwtPayload } from "@Services/common/AuthService/AuthService"; import { Customer } from "le-coffre-resources/dist/SuperAdmin"; @@ -28,7 +34,7 @@ export default class AuthController extends ApiController { this.httpNotFoundRequest(response, "Customer not found"); return; } - this.httpSuccess(response, { partialPhoneNumber: customer.contact?.cell_phone_number.slice(-4) }); + this.httpSuccess(response, { partialPhoneNumber: customer.contact?.cell_phone_number.replace(/\s/g, "").slice(-4) }); } catch (error) { if (error instanceof SmsNotExpiredError) { this.httpTooEarlyRequest(response, error.message); @@ -60,56 +66,28 @@ export default class AuthController extends ApiController { return; } - let customer = await this.customerService.getOne({ - where: { - contact: { - email, - }, - }, - include: { - contact: true, - }, - }); - - if (!customer) { - this.httpNotFoundRequest(response, "Customer not found"); - return; - } - - if (!customer.smsCode) { - this.httpBadRequest(response, "No sms code found"); - return; - } - - if (!customer.smsCodeExpire || new Date().getTime() > customer.smsCodeExpire.getTime()) { - this.httpBadRequest(response, "Sms code expired"); - return; - } - - if (customer.smsCode !== smsCode) { - this.httpBadRequest(response, "Invalid sms code"); - return; - } - - if (!customer.password) { - this.httpBadRequest(response, "Customer not registered"); - return; - } - - // compare password to the hash - const isPasswordValid = await this.authService.comparePassword(password, customer.password); - if (!isPasswordValid) { - this.httpBadRequest(response, "Invalid password"); - return; - } - - const customerHydrated = Customer.hydrate(customer); - const payload = await this.authService.getCustomerJwtPayload([customerHydrated]); - const accessToken = this.authService.generateAccessToken(payload); - const refreshToken = this.authService.generateRefreshToken(payload); try { + const customer = await this.customerService.login(email, smsCode, password); + if (!customer) { + this.httpBadRequest(response, "Customer not found"); + return; + } + const customerHydrated = Customer.hydrate(customer); + const payload = await this.authService.getCustomerJwtPayload([customerHydrated]); + const accessToken = this.authService.generateAccessToken(payload); + const refreshToken = this.authService.generateRefreshToken(payload); this.httpSuccess(response, { accessToken, refreshToken }); } catch (error) { + if (error instanceof TotpCodeExpiredError || error instanceof NotRegisteredCustomerError) { + this.httpBadRequest(response, error.message); + return; + } + + if (error instanceof InvalidTotpCodeError || error instanceof InvalidPasswordError) { + this.httpUnauthorized(response, error.message); + return; + } + console.log(error); this.httpInternalError(response); return; diff --git a/src/services/customer/CustomersService/CustomersService.ts b/src/services/customer/CustomersService/CustomersService.ts index 9b8dae07..85aca519 100644 --- a/src/services/customer/CustomersService/CustomersService.ts +++ b/src/services/customer/CustomersService/CustomersService.ts @@ -1,6 +1,7 @@ import { Customers, Prisma } from "@prisma/client"; import CustomersRepository from "@Repositories/CustomersRepository"; import BaseService from "@Services/BaseService"; +import AuthService from "@Services/common/AuthService/AuthService"; import { Customer } from "le-coffre-resources/dist/Notary"; import { Service } from "typedi"; @@ -9,9 +10,33 @@ export class SmsNotExpiredError extends Error { super("SMS code not expired"); } } + +export class TotpCodeExpiredError extends Error { + constructor() { + super("Totp code not found or expired"); + } +} + +export class InvalidTotpCodeError extends Error { + constructor() { + super("Invalid Totp code"); + } +} + +export class NotRegisteredCustomerError extends Error { + constructor() { + super("Customer not registered"); + } +} + +export class InvalidPasswordError extends Error { + constructor() { + super("Invalid password"); + } +} @Service() export default class CustomersService extends BaseService { - constructor(private customerRepository: CustomersRepository) { + constructor(private customerRepository: CustomersRepository, private authService: AuthService) { super(); } @@ -111,4 +136,41 @@ export default class CustomersService extends BaseService { }, }); } + + /** + * + * @description : Login a customer + * 1: Check if the customer exists + * 2: Check if the SMS code is existing and is not expired + * 3: Check if the SMS code is valid + * 4: Check if the user has a password or it's their first login + * 5: Check if the password is valid + * 6: Return the customer + * @param email + * @param smsCode + * @param password + * @returns Customer | null + */ + public async login(email: string, smsCode: string, password: string): Promise { + // 1: Check if the customer exists + const customer = await this.getByEmail(email); + if (!customer) return null; + + // 2: Check if the SMS code is existing and is not expired + if (!customer.smsCode || !customer.smsCodeExpire || new Date().getTime() > customer.smsCodeExpire.getTime()) + throw new TotpCodeExpiredError(); + + // 3: Check if the SMS code is valid + if (customer.smsCode !== smsCode) throw new InvalidTotpCodeError(); + + // 4: Check if the user has a password or it's their first login + if (!customer.password) throw new NotRegisteredCustomerError(); + + // 5: Check if the password is valid + const isPasswordValid = await this.authService.comparePassword(password, customer.password); + if (!isPasswordValid) throw new InvalidPasswordError(); + + // 6: Return the customer + return customer; + } }