✨ Refacto auth controller
This commit is contained in:
parent
a3deb8dc23
commit
fc8b86b7ca
@ -3,7 +3,7 @@ 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 from "@Services/customer/CustomersService/CustomersService";
|
||||
import CustomersService, { SmsNotExpiredError } from "@Services/customer/CustomersService/CustomersService";
|
||||
import AuthService, { ICustomerJwtPayload } from "@Services/common/AuthService/AuthService";
|
||||
import { Customer } from "le-coffre-resources/dist/SuperAdmin";
|
||||
|
||||
@ -14,61 +14,28 @@ export default class AuthController extends ApiController {
|
||||
super();
|
||||
}
|
||||
|
||||
@Post("/api/v1/customer/pre-login")
|
||||
protected async preLogin(req: Request, response: Response) {
|
||||
@Post("/api/v1/customer/login/mail/verify-sms")
|
||||
protected async mailVerifySms(req: Request, response: Response) {
|
||||
const email = req.body["email"];
|
||||
if (!email) {
|
||||
this.httpBadRequest(response, "Email is required");
|
||||
return;
|
||||
}
|
||||
|
||||
const customer = await this.customerService.getOne({
|
||||
where: {
|
||||
contact: {
|
||||
email,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
contact: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!customer) {
|
||||
this.httpNotFoundRequest(response, "Customer not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// if no sms code has been generated, generate it
|
||||
// if code has expired, regenerate it
|
||||
const now = new Date().getTime();
|
||||
if (customer.smsCodeExpire && now < customer.smsCodeExpire.getTime()) {
|
||||
this.httpBadRequest(response, "Last sms code is still valid");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.customerService.generateSmsCode(customer);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
this.httpInternalError(response);
|
||||
}
|
||||
|
||||
if (!customer.password) {
|
||||
try {
|
||||
this.httpSuccess(response, { info: "Sending a sms for first connection" });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
this.httpInternalError(response);
|
||||
const customer = await this.customerService.verifyEmail2FASms(email);
|
||||
if (!customer) {
|
||||
this.httpNotFoundRequest(response, "Customer not found");
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.httpSuccess(response, { info: "Sending a sms for a connection" });
|
||||
this.httpSuccess(response, { partialPhoneNumber: customer.contact?.cell_phone_number.slice(-4) });
|
||||
} catch (error) {
|
||||
if (error instanceof SmsNotExpiredError) {
|
||||
this.httpTooEarlyRequest(response, error.message);
|
||||
return;
|
||||
}
|
||||
console.log(error);
|
||||
this.httpInternalError(response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,10 @@ export default abstract class BaseController {
|
||||
return this.httpResponse(response, HttpCodes.BAD_REQUEST, responseData);
|
||||
}
|
||||
|
||||
protected httpTooEarlyRequest(response: Response, responseData: IResponseData = "Http Too Early Request") {
|
||||
return this.httpResponse(response, HttpCodes.TOO_EARLY, responseData);
|
||||
}
|
||||
|
||||
protected httpValidationError(response: Response, responseData: IResponseData = "Http Validation Error") {
|
||||
return this.httpResponse(response, HttpCodes.VALIDATION_ERROR, responseData);
|
||||
}
|
||||
|
@ -9,5 +9,6 @@ enum HttpCodes {
|
||||
NOT_FOUND = 404,
|
||||
UNAUTHORIZED = 401,
|
||||
FORBIDDEN = 403,
|
||||
TOO_EARLY = 425,
|
||||
}
|
||||
export default HttpCodes;
|
||||
|
@ -4,6 +4,11 @@ import BaseService from "@Services/BaseService";
|
||||
import { Customer } from "le-coffre-resources/dist/Notary";
|
||||
import { Service } from "typedi";
|
||||
|
||||
export class SmsNotExpiredError extends Error {
|
||||
constructor() {
|
||||
super("SMS code not expired");
|
||||
}
|
||||
}
|
||||
@Service()
|
||||
export default class CustomersService extends BaseService {
|
||||
constructor(private customerRepository: CustomersRepository) {
|
||||
@ -26,25 +31,58 @@ export default class CustomersService extends BaseService {
|
||||
return this.customerRepository.findOne(query);
|
||||
}
|
||||
|
||||
public async userExistsByEmail(email: string): Promise<boolean> {
|
||||
return !!(await this.getByEmail(email));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Generate a SMS code for a customer
|
||||
* @throws {Error}
|
||||
* @description : Send SMS to verify the email of a customer (2FA)
|
||||
* 1: Check if the customer exists
|
||||
* 2: Check if the SMS code 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 generateSmsCode(customer: Customer) {
|
||||
const smsCode = Math.floor(100000 + Math.random() * 900000);
|
||||
const now = new Date();
|
||||
public async verifyEmail2FASms(email: string): Promise<Customer | null> {
|
||||
const customer = await this.getByEmail(email);
|
||||
if (!customer) return null;
|
||||
const now = new Date().getTime();
|
||||
// Check if the SMS code is still valid
|
||||
if (customer.smsCodeExpire && now < customer.smsCodeExpire.getTime()) throw new SmsNotExpiredError();
|
||||
|
||||
const totpPin = this.generateTotp();
|
||||
|
||||
await this.saveTotpPin(customer, totpPin, new Date(now + 5 * 60000));
|
||||
|
||||
// Send the SMS code to the customer
|
||||
await this.sendSmsCodeToCustomer(totpPin, customer);
|
||||
return customer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Saves a TotpPin in database
|
||||
*/
|
||||
private async saveTotpPin(customer: Customer, totpPin: number, expireAt: Date) {
|
||||
return await this.customerRepository.update(
|
||||
customer.uid as string,
|
||||
Customer.hydrate<Customer>({
|
||||
...customer,
|
||||
}),
|
||||
{
|
||||
smsCode: smsCode.toString(),
|
||||
smsCodeExpire: new Date(now.getTime() + 5 * 60000),
|
||||
smsCode: totpPin.toString(),
|
||||
smsCodeExpire: expireAt,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private generateTotp() {
|
||||
return Math.floor(100000 + Math.random() * 900000);
|
||||
}
|
||||
|
||||
private async sendSmsCodeToCustomer(totpPin: number, customer: Customer) {
|
||||
console.log(totpPin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Set password for a customer
|
||||
* @throws {Error} If customer cannot be updated
|
||||
@ -60,4 +98,17 @@ export default class CustomersService extends BaseService {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private getByEmail(email: string) {
|
||||
return this.customerRepository.findOne({
|
||||
where: {
|
||||
contact: {
|
||||
email,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
contact: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user