✨ Verify codes reasons and add a route for password forgotten
This commit is contained in:
parent
bbf480fbda
commit
82d24a71a4
@ -45,6 +45,31 @@ export default class AuthController extends ApiController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post("/api/v1/customer/auth/ask-new-password")
|
||||||
|
protected async askNewPassword(req: Request, response: Response) {
|
||||||
|
const email = req.body["email"];
|
||||||
|
if (!email) {
|
||||||
|
this.httpBadRequest(response, "Email is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const customer = await this.customerService.generateCodeForNewPassword(email);
|
||||||
|
if (!customer) {
|
||||||
|
this.httpNotFoundRequest(response, "Customer not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(error);
|
||||||
|
this.httpInternalError(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Post("/api/v1/customer/auth/login")
|
@Post("/api/v1/customer/auth/login")
|
||||||
protected async login(req: Request, response: Response) {
|
protected async login(req: Request, response: Response) {
|
||||||
const email = req.body["email"];
|
const email = req.body["email"];
|
||||||
|
@ -102,12 +102,45 @@ export default class CustomersService extends BaseService {
|
|||||||
return customer;
|
return customer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description : Send SMS to verify the email of a customer (2FA)
|
||||||
|
* 1: Check if the customer exists
|
||||||
|
* 2: Check in the array of totpCodes if one is still valid
|
||||||
|
* 3: Generate a new SMS code
|
||||||
|
* 4: Save the SMS code in database
|
||||||
|
* 5: Send the SMS code to the customer
|
||||||
|
*/
|
||||||
|
public async generateCodeForNewPassword(email: string): Promise<Customer | null> {
|
||||||
|
// 1: Check if the customer exists
|
||||||
|
const customer = await this.getByEmail(email);
|
||||||
|
if (!customer) return null;
|
||||||
|
const now = new Date().getTime();
|
||||||
|
|
||||||
|
const customerHydrated = Customer.hydrate<Customer>(customer);
|
||||||
|
|
||||||
|
// 2: Check in the array of totpCodes if one is still valid
|
||||||
|
const validTotpCode = customerHydrated.totpCodes?.find((totpCode) => {
|
||||||
|
return totpCode.expire_at && totpCode.expire_at.getTime() > now;
|
||||||
|
});
|
||||||
|
if (validTotpCode) throw new SmsNotExpiredError();
|
||||||
|
|
||||||
|
// 3: Generate a new SMS code
|
||||||
|
const totpPin = this.generateTotp();
|
||||||
|
|
||||||
|
// 4: Save the SMS code in database
|
||||||
|
await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60000), TotpCodesReasons.RESET_PASSWORD);
|
||||||
|
|
||||||
|
// 5: Send the SMS code to the customer
|
||||||
|
await this.sendSmsCodeToCustomer(totpPin, customer);
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description : Set the password of a customer when it's the first time they connect
|
* @description : Set the password of a customer when it's the first time they connect
|
||||||
* 1: Check if the customer exists
|
* 1: Check if the customer exists
|
||||||
* 2: Check if the password is already set
|
* 2: Check if a totp code is existing and is not expired in the array
|
||||||
* 3: Check if a totp code is existing and is not expired in the array
|
* 3: Check if the SMS code is valid
|
||||||
* 4: Check if the SMS code is valid
|
* 4: Check the totpcode reason is valid
|
||||||
* 5: Disable the totp code used
|
* 5: Disable the totp code used
|
||||||
* 6: Hash the password
|
* 6: Hash the 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
|
||||||
@ -121,19 +154,26 @@ export default class CustomersService extends BaseService {
|
|||||||
const customer = await this.getByEmail(email);
|
const customer = await this.getByEmail(email);
|
||||||
if (!customer) return null;
|
if (!customer) return null;
|
||||||
|
|
||||||
// 2: Check if the password is already set
|
|
||||||
if (customer.password) throw new PasswordAlreadySetError();
|
|
||||||
|
|
||||||
const customerHydrated = Customer.hydrate<Customer>(customer);
|
const customerHydrated = Customer.hydrate<Customer>(customer);
|
||||||
// 3: Check if a totp code is existing and is not expired in the array
|
// 2: Check if a totp code is existing and is not expired in the array
|
||||||
const validTotpCode = customerHydrated.totpCodes?.find((totpCode) => {
|
const validTotpCode = customerHydrated.totpCodes?.find((totpCode) => {
|
||||||
return totpCode.expire_at && new Date().getTime() < totpCode.expire_at.getTime();
|
return totpCode.expire_at && new Date().getTime() < totpCode.expire_at.getTime();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!validTotpCode) throw new TotpCodeExpiredError();
|
if (!validTotpCode) throw new TotpCodeExpiredError();
|
||||||
|
|
||||||
// 4: Check if the SMS code is valid
|
// 3: Check if the SMS code is valid
|
||||||
if (validTotpCode.code !== totpCode) throw new InvalidTotpCodeError();
|
if (validTotpCode.code !== totpCode) throw new InvalidTotpCodeError();
|
||||||
|
|
||||||
|
// 4: Check the totpcode reason is valid
|
||||||
|
// If the customer already has a password, the reason must be RESET_PASSWORD
|
||||||
|
// If the customer doesn't have a password, the reason must be FIRST_LOGIN
|
||||||
|
if (
|
||||||
|
(customer.password && validTotpCode.reason !== TotpCodesReasons.RESET_PASSWORD) ||
|
||||||
|
(!customer.password && validTotpCode.reason !== TotpCodesReasons.FIRST_LOGIN)
|
||||||
|
)
|
||||||
|
throw new InvalidTotpCodeError();
|
||||||
|
|
||||||
// 5: Disable the totp code used
|
// 5: Disable the totp code used
|
||||||
await this.totpCodesRepository.disable(validTotpCode);
|
await this.totpCodesRepository.disable(validTotpCode);
|
||||||
|
|
||||||
@ -167,7 +207,7 @@ export default class CustomersService extends BaseService {
|
|||||||
|
|
||||||
// 2: Check if a totp code is existing and is not expired in the array
|
// 2: Check if a totp code is existing and is not expired in the array
|
||||||
const validTotpCode = customerHydrated.totpCodes?.find((totpCode) => {
|
const validTotpCode = customerHydrated.totpCodes?.find((totpCode) => {
|
||||||
return totpCode.expire_at && new Date().getTime() < totpCode.expire_at.getTime();
|
return totpCode.expire_at && new Date().getTime() < totpCode.expire_at.getTime() && totpCode.reason === TotpCodesReasons.LOGIN;
|
||||||
});
|
});
|
||||||
if (!validTotpCode) throw new TotpCodeExpiredError();
|
if (!validTotpCode) throw new TotpCodeExpiredError();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user