From af4f51eaabe9554e12cd846086f18be46e17728c Mon Sep 17 00:00:00 2001 From: Maxime Lalo Date: Fri, 1 Dec 2023 09:49:13 +0100 Subject: [PATCH] :sparkles: Totpcode resend by uid --- src/app/api/customer/AuthController.ts | 28 ++++++++++++------- .../CustomersService/CustomersService.ts | 24 ++++++++-------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/app/api/customer/AuthController.ts b/src/app/api/customer/AuthController.ts index 631dce4c..f4067dd9 100644 --- a/src/app/api/customer/AuthController.ts +++ b/src/app/api/customer/AuthController.ts @@ -30,17 +30,16 @@ export default class AuthController extends ApiController { } try { - const customer = await this.customerService.verifyEmail2FASms(email); - if (!customer) { + const res = await this.customerService.verifyEmail2FASms(email); + if (!res) { this.httpNotFoundRequest(response, "Customer not found"); return; } - this.httpSuccess(response, { partialPhoneNumber: customer.contact?.cell_phone_number.replace(/\s/g, "").slice(-4) }); + this.httpSuccess(response, { + partialPhoneNumber: res.customer.contact?.cell_phone_number.replace(/\s/g, "").slice(-4), + totpCodeUid: res.totpCode.uid, + }); } catch (error) { - if (error instanceof SmsNotExpiredError) { - this.httpTooEarlyRequest(response, error.message); - return; - } console.log(error); this.httpInternalError(response); } @@ -207,18 +206,27 @@ export default class AuthController extends ApiController { @Post("/api/v1/customer/auth/send-another-code") protected async sendAnotherCode(req: Request, response: Response) { const email = req.body["email"]; + const totpCodeUid = req.body["totpCodeUid"]; if (!email) { this.httpBadRequest(response, "email is required"); return; } + if (!totpCodeUid) { + this.httpBadRequest(response, "totpCodeUid is required"); + return; + } + try { - const customer = await this.customerService.askAnotherCode(email); - if (!customer) { + const res = await this.customerService.askAnotherCode(email, totpCodeUid); + if (!res) { this.httpNotFoundRequest(response, "Customer not found"); return; } - this.httpSuccess(response, { partialPhoneNumber: customer.contact?.cell_phone_number.replace(/\s/g, "").slice(-4) }); + this.httpSuccess(response, { + partialPhoneNumber: res.customer.contact?.cell_phone_number.replace(/\s/g, "").slice(-4), + totpCodeUid: res.totpCode.uid, + }); } catch (error) { if (error instanceof TooSoonForNewCode || error instanceof TotpCodeExpiredError) { this.httpUnauthorized(response, error.message); diff --git a/src/services/customer/CustomersService/CustomersService.ts b/src/services/customer/CustomersService/CustomersService.ts index 0a81ee35..03c087df 100644 --- a/src/services/customer/CustomersService/CustomersService.ts +++ b/src/services/customer/CustomersService/CustomersService.ts @@ -88,7 +88,7 @@ export default class CustomersService extends BaseService { * 4: Save the SMS code in database * 5: Send the SMS code to the customer */ - public async verifyEmail2FASms(email: string): Promise { + public async verifyEmail2FASms(email: string): Promise<{ customer: Customer; totpCode: TotpCodes } | null> { // 1: Check if the customer exists const customer = await this.getByEmail(email); if (!customer) return null; @@ -100,18 +100,18 @@ export default class CustomersService extends BaseService { const validTotpCode = customerHydrated.totpCodes?.find((totpCode) => { return totpCode.expire_at && totpCode.expire_at.getTime() > now; }); - if (validTotpCode) throw new SmsNotExpiredError(); + if (validTotpCode) return { customer, totpCode: validTotpCode }; // 3: Generate a new SMS code const totpPin = this.generateTotp(); const reason = customer.password ? TotpCodesReasons.LOGIN : TotpCodesReasons.FIRST_LOGIN; // 4: Save the SMS code in database - await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60000), reason); + const totpCode = await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60000), reason); // 5: Send the SMS code to the customer await this.sendSmsCodeToCustomer(totpPin, customer); - return customer; + return { customer, totpCode }; } /** @@ -258,7 +258,7 @@ export default class CustomersService extends BaseService { ); } - public async askAnotherCode(email: string): Promise { + public async askAnotherCode(email: string, totpCodeUid: string): Promise<{ customer: Customer; totpCode: TotpCodes } | null> { // 1: Check if the customer exists const customer = await this.getByEmail(email); if (!customer) return null; @@ -267,26 +267,26 @@ export default class CustomersService extends BaseService { const customerHydrated = Customer.hydrate(customer); // 2: Get last code sent - const lastCode = customerHydrated.totpCodes?.find((totpCode) => { - return totpCode.expire_at && totpCode.expire_at.getTime() > now; + const totpCodeToResend = customerHydrated.totpCodes?.find((totpCode) => { + return totpCode.uid && totpCodeUid; }); - if (!lastCode) throw new TotpCodeExpiredError(); + if (!totpCodeToResend) throw new TotpCodeExpiredError(); // 3: Check if it was created more than 30 seconds ago - if (lastCode.created_at && lastCode.created_at.getTime() > now - 30000) throw new TooSoonForNewCode(); + if (totpCodeToResend.created_at && totpCodeToResend.created_at.getTime() > now - 30000) throw new TooSoonForNewCode(); // 4: Generate a new SMS code const totpPin = this.generateTotp(); // 5: Disable the old code - await this.totpCodesRepository.disable(lastCode); + await this.totpCodesRepository.disable(totpCodeToResend); // 6: Save the SMS code in database - await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60000), lastCode.reason!); + const totpCode = await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60000), totpCodeToResend.reason!); // 7: Send the SMS code to the customer await this.sendSmsCodeToCustomer(totpPin, customer); - return customer; + return { customer, totpCode }; } /**