diff --git a/package.json b/package.json index 33f4337d..7d1cb4f0 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "file-type-checker": "^1.0.8", "fp-ts": "^2.16.1", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.99", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.102", "module-alias": "^2.2.2", "monocle-ts": "^2.3.13", "multer": "^1.4.5-lts.1", diff --git a/src/common/databases/migrations/20231201150833_resent_totp/migration.sql b/src/common/databases/migrations/20231201150833_resent_totp/migration.sql new file mode 100644 index 00000000..bf3805f6 --- /dev/null +++ b/src/common/databases/migrations/20231201150833_resent_totp/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "totp_codes" ADD COLUMN "resent" BOOLEAN NOT NULL DEFAULT false; diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index be5c663b..77d5f2d3 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -348,6 +348,7 @@ model TotpCodes { customer_uid String @db.VarChar(255) code String @db.VarChar(255) reason TotpCodesReasons @default(LOGIN) + resent Boolean @default(false) expire_at DateTime? @default(now()) created_at DateTime? @default(now()) updated_at DateTime? @updatedAt diff --git a/src/common/repositories/TotpCodesRepository.ts b/src/common/repositories/TotpCodesRepository.ts index 8d1ebf48..4a78e4e5 100644 --- a/src/common/repositories/TotpCodesRepository.ts +++ b/src/common/repositories/TotpCodesRepository.ts @@ -48,6 +48,7 @@ export default class TotpCodesRepository extends BaseRepository { }, }, expire_at: totpCode.expire_at!, + resent: totpCode.resent!, }, }; @@ -64,6 +65,7 @@ export default class TotpCodesRepository extends BaseRepository { }, data: { expire_at: new Date(), + resent: true, }, }); } diff --git a/src/services/customer/CustomersService/CustomersService.ts b/src/services/customer/CustomersService/CustomersService.ts index cf03f0c8..67dc09a5 100644 --- a/src/services/customer/CustomersService/CustomersService.ts +++ b/src/services/customer/CustomersService/CustomersService.ts @@ -1,10 +1,10 @@ import { BackendVariables } from "@Common/config/variables/Variables"; -import { Customers, Prisma } from "@prisma/client"; +import { Customers, Prisma, TotpCodes } from "@prisma/client"; import CustomersRepository from "@Repositories/CustomersRepository"; import TotpCodesRepository from "@Repositories/TotpCodesRepository"; import BaseService from "@Services/BaseService"; import AuthService from "@Services/common/AuthService/AuthService"; -import TotpCodes, { TotpCodesReasons } from "le-coffre-resources/dist/Customer/TotpCodes"; +import TotpCodesResource, { TotpCodesReasons } from "le-coffre-resources/dist/Customer/TotpCodes"; import { Customer } from "le-coffre-resources/dist/Notary"; import { Service } from "typedi"; import OvhService from "@Services/common/OvhService/OvhService"; @@ -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<{ customer: Customer; totpCode: TotpCodes } | null> { + public async verifyEmail2FASms(email: string): Promise<{ customer: Customer; totpCode: TotpCodesResource } | null> { // 1: Check if the customer exists const customer = await this.getByEmail(email); if (!customer) return null; @@ -108,10 +108,16 @@ export default class CustomersService extends BaseService { const reason = customer.password ? TotpCodesReasons.LOGIN : TotpCodesReasons.FIRST_LOGIN; // 4: Save the SMS code in database const totpCode = await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60000), reason); - + if (!totpCode) return null; // 5: Send the SMS code to the customer await this.sendSmsCodeToCustomer(totpPin, customer); - return { customer, totpCode }; + return { + customer, + totpCode: TotpCodesResource.hydrate({ + ...totpCode, + reason: totpCode.reason as TotpCodesReasons, + }), + }; } /** @@ -273,7 +279,8 @@ export default class CustomersService extends BaseService { if (!totpCodeToResend) throw new TotpCodeExpiredError(); // 3: Check if it was created more than 30 seconds ago - if (totpCodeToResend.created_at && totpCodeToResend.created_at.getTime() > now - 30000) throw new TooSoonForNewCode(); + if (totpCodeToResend.created_at && totpCodeToResend.created_at.getTime() > now - 30000 && totpCodeToResend.resent) + throw new TooSoonForNewCode(); // 4: Generate a new SMS code const totpPin = this.generateTotp(); @@ -282,7 +289,7 @@ export default class CustomersService extends BaseService { await this.totpCodesRepository.disable(totpCodeToResend); // 6: Save the SMS code in database - const totpCode = await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60000), totpCodeToResend.reason!); + const totpCode = await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60000), totpCodeToResend.reason!, true); // 7: Send the SMS code to the customer await this.sendSmsCodeToCustomer(totpPin, customer); @@ -322,22 +329,23 @@ export default class CustomersService extends BaseService { /** * @description : Saves a TotpPin in database */ - private async saveTotpPin(customer: Customer, totpPin: number, expireAt: Date, reason: TotpCodesReasons) { + private async saveTotpPin( + customer: Customer, + totpPin: number, + expireAt: Date, + reason: TotpCodesReasons, + resent?: boolean, + ): Promise { // Create the totpCode in table using repository - await this.totpCodesRepository.create( - TotpCodes.hydrate({ + return await this.totpCodesRepository.create( + TotpCodesResource.hydrate({ reason, customer_uid: customer.uid, customer: Customer.hydrate(customer), created_at: new Date(), code: totpPin.toString(), expire_at: expireAt, - }), - ); - return await this.customerRepository.update( - customer.uid as string, - Customer.hydrate({ - ...customer, + resent: resent || false, }), ); } @@ -371,7 +379,7 @@ export default class CustomersService extends BaseService { * @param email * @returns */ - public async verifyTotpCode(totpCode: string, email: string): Promise { + public async verifyTotpCode(totpCode: string, email: string): Promise { // 1: Check if the customer exists const customer = await this.getByEmail(email); if (!customer) return null;