Merge branch 'dev' into fixMigrations

This commit is contained in:
Vins 2023-12-05 11:10:05 +01:00
commit 5a7f3c88e2
10 changed files with 144 additions and 121 deletions

View File

@ -57,7 +57,7 @@
"file-type-checker": "^1.0.8", "file-type-checker": "^1.0.8",
"fp-ts": "^2.16.1", "fp-ts": "^2.16.1",
"jsonwebtoken": "^9.0.0", "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.104",
"module-alias": "^2.2.2", "module-alias": "^2.2.2",
"monocle-ts": "^2.3.13", "monocle-ts": "^2.3.13",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",

View File

@ -6,7 +6,6 @@ import CustomersService, {
InvalidPasswordError, InvalidPasswordError,
InvalidTotpCodeError, InvalidTotpCodeError,
NotRegisteredCustomerError, NotRegisteredCustomerError,
PasswordAlreadySetError,
SmsNotExpiredError, SmsNotExpiredError,
TooSoonForNewCode, TooSoonForNewCode,
TotpCodeExpiredError, TotpCodeExpiredError,
@ -126,22 +125,28 @@ export default class AuthController extends ApiController {
const password = req.body["password"]; const password = req.body["password"];
if (!email) { if (!email) {
this.httpBadRequest(response, "Email is required"); this.httpBadRequest(response, "email is required");
return; return;
} }
if (!totpCode) { if (!totpCode) {
this.httpBadRequest(response, "Sms code is required"); this.httpBadRequest(response, "totpCode is required");
return; return;
} }
if (!password) { if (!password) {
this.httpBadRequest(response, "Password is required"); this.httpBadRequest(response, "password is required");
return;
}
const passwordRegex = new RegExp(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$/);
if (!passwordRegex.test(password)) {
this.httpBadRequest(response, "Password must contain at least 8 characters, 1 uppercase, 1 lowercase and 1 number");
return; return;
} }
try { try {
const customer = await this.customerService.setFirstPassword(email, totpCode, password); const customer = await this.customerService.setPassword(email, totpCode, password);
if (!customer) { if (!customer) {
this.httpBadRequest(response, "Customer not found"); this.httpBadRequest(response, "Customer not found");
return; return;
@ -153,7 +158,7 @@ export default class AuthController extends ApiController {
const refreshToken = this.authService.generateRefreshToken(payload); const refreshToken = this.authService.generateRefreshToken(payload);
this.httpSuccess(response, { accessToken, refreshToken }); this.httpSuccess(response, { accessToken, refreshToken });
} catch (error) { } catch (error) {
if (error instanceof TotpCodeExpiredError || error instanceof PasswordAlreadySetError) { if (error instanceof TotpCodeExpiredError) {
this.httpBadRequest(response, error.message); this.httpBadRequest(response, error.message);
return; return;
} }

View File

@ -121,6 +121,9 @@ export class BackendVariables {
@IsNotEmpty() @IsNotEmpty()
public readonly OVH_CONSUMER_KEY!: string; public readonly OVH_CONSUMER_KEY!: string;
@IsNotEmpty()
public readonly OVH_SMS_SERVICE_NAME!: string;
@IsNotEmpty() @IsNotEmpty()
public readonly SMS_FACTOR_TOKEN!: string; public readonly SMS_FACTOR_TOKEN!: string;
@ -165,6 +168,7 @@ export class BackendVariables {
this.OVH_APP_KEY = process.env["OVH_APP_KEY"]!; this.OVH_APP_KEY = process.env["OVH_APP_KEY"]!;
this.OVH_APP_SECRET = process.env["OVH_APP_SECRET"]!; this.OVH_APP_SECRET = process.env["OVH_APP_SECRET"]!;
this.OVH_CONSUMER_KEY = process.env["OVH_CONSUMER_KEY"]!; this.OVH_CONSUMER_KEY = process.env["OVH_CONSUMER_KEY"]!;
this.OVH_SMS_SERVICE_NAME = process.env["OVH_SMS_SERVICE_NAME"]!;
this.SMS_FACTOR_TOKEN = process.env["SMS_FACTOR_TOKEN"]!; this.SMS_FACTOR_TOKEN = process.env["SMS_FACTOR_TOKEN"]!;

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "totp_codes" ADD COLUMN "resent" BOOLEAN NOT NULL DEFAULT false;

View File

@ -348,6 +348,7 @@ model TotpCodes {
customer_uid String @db.VarChar(255) customer_uid String @db.VarChar(255)
code String @db.VarChar(255) code String @db.VarChar(255)
reason TotpCodesReasons @default(LOGIN) reason TotpCodesReasons @default(LOGIN)
resent Boolean @default(false)
expire_at DateTime? @default(now()) expire_at DateTime? @default(now())
created_at DateTime? @default(now()) created_at DateTime? @default(now())
updated_at DateTime? @updatedAt updated_at DateTime? @updatedAt

View File

@ -173,8 +173,8 @@ export default async function main() {
first_name: "Angela", first_name: "Angela",
last_name: "Dubois", last_name: "Dubois",
email: "angela.dubois@gmail.com", email: "angela.dubois@gmail.com",
phone_number: "06 12 34 56 78", phone_number: "+33785186013",
cell_phone_number: "06 12 34 56 78", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -185,8 +185,8 @@ export default async function main() {
first_name: "Maxime", first_name: "Maxime",
last_name: "Lalo", last_name: "Lalo",
email: "maxime.lalo@smart-chain.fr", email: "maxime.lalo@smart-chain.fr",
phone_number: "06 23 45 67 89", phone_number: "+33785186013",
cell_phone_number: "06 23 45 67 89", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -197,8 +197,8 @@ export default async function main() {
first_name: "Vincent", first_name: "Vincent",
last_name: "Alamelle", last_name: "Alamelle",
email: "vincent.alamelle@smart-chain.fr", email: "vincent.alamelle@smart-chain.fr",
phone_number: "06 34 56 78 90", phone_number: "+33785186013",
cell_phone_number: "06 34 56 78 90", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -209,8 +209,8 @@ export default async function main() {
first_name: "Melissa", first_name: "Melissa",
last_name: "Desde", last_name: "Desde",
email: "melissa.desde@smart-chain.fr", email: "melissa.desde@smart-chain.fr",
phone_number: "06 45 67 89 01", phone_number: "+33785186013",
cell_phone_number: "06 45 67 89 01", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -221,8 +221,8 @@ export default async function main() {
first_name: "Maxime", first_name: "Maxime",
last_name: "Leroy", last_name: "Leroy",
email: "maxime.leroy@hotmail.fr", email: "maxime.leroy@hotmail.fr",
phone_number: "06 56 78 90 12", phone_number: "+33785186013",
cell_phone_number: "06 56 78 90 12", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -233,8 +233,8 @@ export default async function main() {
first_name: "Paul", first_name: "Paul",
last_name: "Dupont", last_name: "Dupont",
email: "paul.dupont@outlook.com", email: "paul.dupont@outlook.com",
phone_number: "06 67 89 01 23", phone_number: "+33785186013",
cell_phone_number: "06 67 89 01 23", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -245,8 +245,8 @@ export default async function main() {
first_name: "Jean", first_name: "Jean",
last_name: "Dubignot", last_name: "Dubignot",
email: "jean.dubignot@gmail.com", email: "jean.dubignot@gmail.com",
phone_number: "06 78 90 12 34", phone_number: "+33785186013",
cell_phone_number: "06 78 90 12 34", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -257,8 +257,8 @@ export default async function main() {
first_name: "Vincent", first_name: "Vincent",
last_name: "Martin", last_name: "Martin",
email: "vincent.martin@gmail.com", email: "vincent.martin@gmail.com",
phone_number: "06 89 01 23 45", phone_number: "+33785186013",
cell_phone_number: "06 89 01 23 45", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -269,8 +269,8 @@ export default async function main() {
first_name: "Lucie", first_name: "Lucie",
last_name: "Chevalier", last_name: "Chevalier",
email: "lucie.chevalier@outlook.com", email: "lucie.chevalier@outlook.com",
phone_number: "07 12 34 56 78", phone_number: "+33785186013",
cell_phone_number: "07 12 34 56 78", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -281,8 +281,8 @@ export default async function main() {
first_name: "Sébastien", first_name: "Sébastien",
last_name: "Dubois", last_name: "Dubois",
email: "sebastien.dubois@gmail.com", email: "sebastien.dubois@gmail.com",
phone_number: "07 23 45 67 89", phone_number: "+33785186013",
cell_phone_number: "07 23 45 67 89", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -293,8 +293,8 @@ export default async function main() {
first_name: "Mathilde", first_name: "Mathilde",
last_name: "Durand", last_name: "Durand",
email: "mathilde.durand@gmail.com", email: "mathilde.durand@gmail.com",
phone_number: "07 34 56 78 90", phone_number: "+33785186013",
cell_phone_number: "07 34 56 78 90", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -305,8 +305,8 @@ export default async function main() {
first_name: "Antoine", first_name: "Antoine",
last_name: "Bernard", last_name: "Bernard",
email: "antoine.bernard@outlook.com", email: "antoine.bernard@outlook.com",
phone_number: "07 45 67 89 01", phone_number: "+33785186013",
cell_phone_number: "07 45 67 89 01", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -317,8 +317,8 @@ export default async function main() {
first_name: "Camille", first_name: "Camille",
last_name: "Laurent", last_name: "Laurent",
email: "camille.laurent@gmail.com", email: "camille.laurent@gmail.com",
phone_number: "07 56 78 90 12", phone_number: "+33785186013",
cell_phone_number: "07 56 78 90 12", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -329,8 +329,8 @@ export default async function main() {
first_name: "Julien", first_name: "Julien",
last_name: "Mercier", last_name: "Mercier",
email: "julien.mercier@hotmail.fr", email: "julien.mercier@hotmail.fr",
phone_number: "07 67 89 01 23", phone_number: "+33785186013",
cell_phone_number: "07 67 89 01 23", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -341,8 +341,8 @@ export default async function main() {
first_name: "Charlotte", first_name: "Charlotte",
last_name: "Lefebvre", last_name: "Lefebvre",
email: "charlotte.lefebvre@gmail.com", email: "charlotte.lefebvre@gmail.com",
phone_number: "07 78 90 12 34", phone_number: "+33785186013",
cell_phone_number: "07 78 90 12 34", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -353,8 +353,8 @@ export default async function main() {
first_name: "Caroline", first_name: "Caroline",
last_name: "Pallut", last_name: "Pallut",
email: "caroline.pallut@gmail.com", email: "caroline.pallut@gmail.com",
phone_number: "07 89 01 23 45", phone_number: "+33785186013",
cell_phone_number: "07 89 01 23 45", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -365,8 +365,8 @@ export default async function main() {
first_name: "Nadège", first_name: "Nadège",
last_name: "Gauchet", last_name: "Gauchet",
email: "nedege.gauchet@outlook.com", email: "nedege.gauchet@outlook.com",
phone_number: "06 11 22 33 44", phone_number: "+33785186013",
cell_phone_number: "06 11 22 33 44", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -377,8 +377,8 @@ export default async function main() {
first_name: "Matthieu", first_name: "Matthieu",
last_name: "Bougeard", last_name: "Bougeard",
email: "matthieu.bougeard@gmail.com", email: "matthieu.bougeard@gmail.com",
phone_number: "07 22 33 44 55", phone_number: "+33785186013",
cell_phone_number: "07 22 33 44 55", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -389,8 +389,8 @@ export default async function main() {
first_name: "Cécile", first_name: "Cécile",
last_name: "Celton", last_name: "Celton",
email: "cecile.celton@outlook.com", email: "cecile.celton@outlook.com",
phone_number: "06 55 66 77 88", phone_number: "+33785186013",
cell_phone_number: "06 55 66 77 88", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -401,8 +401,8 @@ export default async function main() {
first_name: "Gwendal", first_name: "Gwendal",
last_name: "Texier", last_name: "Texier",
email: "gwendal.texier@gmail.com", email: "gwendal.texier@gmail.com",
phone_number: "07 88 99 00 11", phone_number: "+33785186013",
cell_phone_number: "07 88 99 00 11", cell_phone_number: "+33785186013",
birthdate: null, birthdate: null,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -1548,7 +1548,7 @@ export default async function main() {
office: offices[0], office: offices[0],
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
} },
]; ];
const deedTypes: DeedType[] = [ const deedTypes: DeedType[] = [

View File

@ -48,6 +48,7 @@ export default class TotpCodesRepository extends BaseRepository {
}, },
}, },
expire_at: totpCode.expire_at!, expire_at: totpCode.expire_at!,
resent: totpCode.resent!,
}, },
}; };
@ -64,6 +65,7 @@ export default class TotpCodesRepository extends BaseRepository {
}, },
data: { data: {
expire_at: new Date(), expire_at: new Date(),
resent: true,
}, },
}); });
} }

View File

@ -15,29 +15,22 @@ export default class OvhService extends BaseService {
consumerKey: this.variables.OVH_CONSUMER_KEY, consumerKey: this.variables.OVH_CONSUMER_KEY,
}); });
ovh.request("GET", "/sms", function (err: any, serviceName: string) { const serviceName = this.variables.OVH_SMS_SERVICE_NAME;
if (err) {
console.log(err, serviceName);
return false;
} else {
console.log("My account SMS is " + serviceName);
// Send a simple SMS with a short number using your serviceName await ovh.request('POST', '/sms/' + serviceName + '/jobs/', {
ovh.request( message: message,
"POST", sender: "LeCoffre",
"/sms/" + serviceName + "/jobs", senderForResponse: true,
{ receivers: [phoneNumber],
message: message, }, (error: any, response: any) => {
senderForResponse: true, if (error) {
receivers: [phoneNumber], console.error('Error sending Ovh Sms');
}, return false;
function (errsend: any, result: any) { } else {
console.log(errsend, result); console.log('SMS sent successfully via Ovh');
}, return true;
);
return true;
} }
}); });
return false; return true;
} }
} }

View File

@ -5,26 +5,28 @@ import axios from "axios";
@Service() @Service()
export default class SmsFactorService extends BaseService { export default class SmsFactorService extends BaseService {
constructor(private variables: BackendVariables) {
constructor(private variables: BackendVariables) {
super(); super();
} }
public async sendSms(phoneNumber: string, message: string): Promise<boolean> { public async sendSms(phoneNumber: string, message: string){
axios.post('https://api.smsfactor.com/send/', { axios
token: this.variables.SMS_FACTOR_TOKEN, .get(
sender: "LeCoffre", "https://api.smsfactor.com/send/simulate?to=" +
to: phoneNumber, phoneNumber +
text: message, "&sender=LeCoffre&text=" +
}).then(response => { message +
console.log('SMS sent successfully:', response.data); "&token=" +
return true; this.variables.SMS_FACTOR_TOKEN,
{},
}) )
.catch(error => { .then((response) => {
console.error('Error sending SMS:', error.response.data); console.log("SMS sent successfully via Sms Factor");
return false; return true;
}); })
return false; .catch((error) => {
} console.error("Error sending Sms Factor SMS");
return false;
});
}
} }

View File

@ -1,10 +1,10 @@
import { BackendVariables } from "@Common/config/variables/Variables"; // 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 CustomersRepository from "@Repositories/CustomersRepository";
import TotpCodesRepository from "@Repositories/TotpCodesRepository"; import TotpCodesRepository from "@Repositories/TotpCodesRepository";
import BaseService from "@Services/BaseService"; import BaseService from "@Services/BaseService";
import AuthService from "@Services/common/AuthService/AuthService"; 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 { Customer } from "le-coffre-resources/dist/Notary";
import { Service } from "typedi"; import { Service } from "typedi";
import OvhService from "@Services/common/OvhService/OvhService"; import OvhService from "@Services/common/OvhService/OvhService";
@ -57,7 +57,7 @@ export default class CustomersService extends BaseService {
private customerRepository: CustomersRepository, private customerRepository: CustomersRepository,
private authService: AuthService, private authService: AuthService,
private totpCodesRepository: TotpCodesRepository, private totpCodesRepository: TotpCodesRepository,
private variables: BackendVariables, // private variables: BackendVariables,
private ovhService: OvhService, private ovhService: OvhService,
private smsFactorService: SmsFactorService, private smsFactorService: SmsFactorService,
) { ) {
@ -88,7 +88,7 @@ export default class CustomersService extends BaseService {
* 4: Save the SMS code in database * 4: Save the SMS code in database
* 5: Send the SMS code to the customer * 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 // 1: Check if the customer exists
const customer = await this.getByEmail(email); const customer = await this.getByEmail(email);
if (!customer) return null; if (!customer) return null;
@ -107,11 +107,18 @@ export default class CustomersService extends BaseService {
const reason = customer.password ? TotpCodesReasons.LOGIN : TotpCodesReasons.FIRST_LOGIN; const reason = customer.password ? TotpCodesReasons.LOGIN : TotpCodesReasons.FIRST_LOGIN;
// 4: Save the SMS code in database // 4: Save the SMS code in database
const totpCode = await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60000), reason); const totpCode = await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60 * 1000), reason);
if (!totpCode) return null;
// 5: Send the SMS code to the customer // 5: Send the SMS code to the customer
// if(this.variables.ENV !== 'dev')
await this.sendSmsCodeToCustomer(totpPin, customer); await this.sendSmsCodeToCustomer(totpPin, customer);
return { customer, totpCode }; return {
customer,
totpCode: TotpCodesResource.hydrate<TotpCodesResource>({
...totpCode,
reason: totpCode.reason as TotpCodesReasons,
}),
};
} }
/** /**
@ -156,6 +163,7 @@ export default class CustomersService extends BaseService {
await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60000), TotpCodesReasons.RESET_PASSWORD); await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60000), TotpCodesReasons.RESET_PASSWORD);
// 5: Send the SMS code to the customer // 5: Send the SMS code to the customer
// if(this.variables.ENV !== 'dev')
await this.sendSmsCodeToCustomer(totpPin, customer); await this.sendSmsCodeToCustomer(totpPin, customer);
return customer; return customer;
} }
@ -174,7 +182,7 @@ export default class CustomersService extends BaseService {
* @param password * @param password
* @returns * @returns
*/ */
public async setFirstPassword(email: string, totpCode: string, password: string): Promise<Customer | null> { public async setPassword(email: string, totpCode: string, password: string): Promise<Customer | null> {
// 1: Check if the customer exists // 1: Check if the customer exists
const customer = await this.getByEmail(email); const customer = await this.getByEmail(email);
if (!customer) return null; if (!customer) return null;
@ -206,7 +214,7 @@ export default class CustomersService extends BaseService {
const hashedPassword = await this.authService.hashPassword(password); const hashedPassword = await this.authService.hashPassword(password);
// 7: Set the password in database and return the result of the update // 7: Set the password in database and return the result of the update
return await this.setPassword(customer, hashedPassword); return await this.setPasswordInDatabase(customer, hashedPassword);
} }
/** /**
@ -266,14 +274,15 @@ export default class CustomersService extends BaseService {
const customerHydrated = Customer.hydrate<Customer>(customer); const customerHydrated = Customer.hydrate<Customer>(customer);
// 2: Get last code sent // 2: Get last code sent and check if it's still valid
const totpCodeToResend = customerHydrated.totpCodes?.find((totpCode) => { const totpCodeToResend = customerHydrated.totpCodes?.find((totpCode) => {
return totpCode.uid === totpCodeUid && totpCode.expire_at && totpCode.expire_at.getTime() > now; return totpCode.uid === totpCodeUid && totpCode.expire_at && totpCode.expire_at.getTime() > now;
}); });
if (!totpCodeToResend) throw new TotpCodeExpiredError(); if (!totpCodeToResend) throw new TotpCodeExpiredError();
// 3: Check if it was created more than 30 seconds ago // 3: Check if it was created more than 30 seconds ago and hasn't been resent yet
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 // 4: Generate a new SMS code
const totpPin = this.generateTotp(); const totpPin = this.generateTotp();
@ -281,10 +290,11 @@ export default class CustomersService extends BaseService {
// 5: Disable the old code // 5: Disable the old code
await this.totpCodesRepository.disable(totpCodeToResend); await this.totpCodesRepository.disable(totpCodeToResend);
// 6: Save the SMS code in database // 6: Save the SMS code in database with the same reason as the old one
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 * 60 * 1000), totpCodeToResend.reason!, true);
// 7: Send the SMS code to the customer // 7: Send the SMS code to the customer
// if(this.variables.ENV !== 'dev')
await this.sendSmsCodeToCustomer(totpPin, customer); await this.sendSmsCodeToCustomer(totpPin, customer);
return { customer, totpCode }; return { customer, totpCode };
} }
@ -293,7 +303,7 @@ export default class CustomersService extends BaseService {
* @description : Set password for a customer * @description : Set password for a customer
* @throws {Error} If customer cannot be updated * @throws {Error} If customer cannot be updated
*/ */
private async setPassword(customer: Customer, password: string) { private async setPasswordInDatabase(customer: Customer, password: string) {
return await this.customerRepository.update( return await this.customerRepository.update(
customer.uid as string, customer.uid as string,
Customer.hydrate<Customer>({ Customer.hydrate<Customer>({
@ -322,22 +332,23 @@ export default class CustomersService extends BaseService {
/** /**
* @description : Saves a TotpPin in database * @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<TotpCodes> {
// Create the totpCode in table using repository // Create the totpCode in table using repository
await this.totpCodesRepository.create( return await this.totpCodesRepository.create(
TotpCodes.hydrate<TotpCodes>({ TotpCodesResource.hydrate<TotpCodesResource>({
reason, reason,
customer_uid: customer.uid, customer_uid: customer.uid,
customer: Customer.hydrate<Customer>(customer), customer: Customer.hydrate<Customer>(customer),
created_at: new Date(), created_at: new Date(),
code: totpPin.toString(), code: totpPin.toString(),
expire_at: expireAt, expire_at: expireAt,
}), resent: resent || false,
);
return await this.customerRepository.update(
customer.uid as string,
Customer.hydrate<Customer>({
...customer,
}), }),
); );
} }
@ -347,17 +358,20 @@ export default class CustomersService extends BaseService {
} }
private async sendSmsCodeToCustomer(totpPin: number, customer: Customer) { private async sendSmsCodeToCustomer(totpPin: number, customer: Customer) {
const message = "Votre code de vérification LEcoffre.io est : " + totpPin.toString();
// Sélectionnez le fournisseur de SMS en fonction de la variable d'environnement // Sélectionnez le fournisseur de SMS en fonction de la variable d'environnement
const selectedProvider = this.variables.SMS_PROVIDER === "OVH" ? this.ovhService : this.smsFactorService; //const selectedProvider = this.variables.SMS_PROVIDER === "OVH" ? this.ovhService : this.smsFactorService;
// Envoi du SMS // Envoi du SMS
if (!customer.contact?.phone_number) return; if (!customer.contact?.cell_phone_number) return;
let success = await selectedProvider.sendSms(customer.contact?.phone_number, totpPin.toString()); let success = await this.ovhService.sendSms(customer.contact?.cell_phone_number, message);
// Si l'envoi échoue, basculez automatiquement sur le second fournisseur // Si l'envoi échoue, basculez automatiquement sur le second fournisseur
if (!success) { if (!success) {
const alternateProvider = this.variables.SMS_PROVIDER === "OVH" ? this.smsFactorService : this.ovhService; //const alternateProvider = this.variables.SMS_PROVIDER === "OVH" ? this.smsFactorService : this.ovhService;
success = await alternateProvider.sendSms(customer.contact?.phone_number, totpPin.toString()); await this.smsFactorService.sendSms(customer.contact?.cell_phone_number, message);
} }
} }
@ -371,7 +385,7 @@ export default class CustomersService extends BaseService {
* @param email * @param email
* @returns * @returns
*/ */
public async verifyTotpCode(totpCode: string, email: string): Promise<TotpCodes | null> { public async verifyTotpCode(totpCode: string, email: string): Promise<TotpCodesResource | null> {
// 1: Check if the customer exists // 1: Check if the customer exists
const customer = await this.getByEmail(email); const customer = await this.getByEmail(email);
if (!customer) return null; if (!customer) return null;