✨ 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 ApiController from "@Common/system/controller-pattern/ApiController";
|
||||||
import { Service } from "typedi";
|
import { Service } from "typedi";
|
||||||
import { EnrollmentResponse } from "@Services/common/Id360Service/Id360Service";
|
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 AuthService, { ICustomerJwtPayload } from "@Services/common/AuthService/AuthService";
|
||||||
import { Customer } from "le-coffre-resources/dist/SuperAdmin";
|
import { Customer } from "le-coffre-resources/dist/SuperAdmin";
|
||||||
|
|
||||||
@ -14,61 +14,28 @@ export default class AuthController extends ApiController {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post("/api/v1/customer/pre-login")
|
@Post("/api/v1/customer/login/mail/verify-sms")
|
||||||
protected async preLogin(req: Request, response: Response) {
|
protected async mailVerifySms(req: Request, response: Response) {
|
||||||
const email = req.body["email"];
|
const email = req.body["email"];
|
||||||
if (!email) {
|
if (!email) {
|
||||||
this.httpBadRequest(response, "Email is required");
|
this.httpBadRequest(response, "Email is required");
|
||||||
return;
|
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 {
|
try {
|
||||||
await this.customerService.generateSmsCode(customer);
|
const customer = await this.customerService.verifyEmail2FASms(email);
|
||||||
} catch (error) {
|
if (!customer) {
|
||||||
console.log(error);
|
this.httpNotFoundRequest(response, "Customer not found");
|
||||||
this.httpInternalError(response);
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (!customer.password) {
|
|
||||||
try {
|
|
||||||
this.httpSuccess(response, { info: "Sending a sms for first connection" });
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
this.httpInternalError(response);
|
|
||||||
}
|
}
|
||||||
return;
|
this.httpSuccess(response, { partialPhoneNumber: customer.contact?.cell_phone_number.slice(-4) });
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.httpSuccess(response, { info: "Sending a sms for a connection" });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error instanceof SmsNotExpiredError) {
|
||||||
|
this.httpTooEarlyRequest(response, error.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
console.log(error);
|
console.log(error);
|
||||||
this.httpInternalError(response);
|
this.httpInternalError(response);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,10 @@ export default abstract class BaseController {
|
|||||||
return this.httpResponse(response, HttpCodes.BAD_REQUEST, responseData);
|
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") {
|
protected httpValidationError(response: Response, responseData: IResponseData = "Http Validation Error") {
|
||||||
return this.httpResponse(response, HttpCodes.VALIDATION_ERROR, responseData);
|
return this.httpResponse(response, HttpCodes.VALIDATION_ERROR, responseData);
|
||||||
}
|
}
|
||||||
|
@ -9,5 +9,6 @@ enum HttpCodes {
|
|||||||
NOT_FOUND = 404,
|
NOT_FOUND = 404,
|
||||||
UNAUTHORIZED = 401,
|
UNAUTHORIZED = 401,
|
||||||
FORBIDDEN = 403,
|
FORBIDDEN = 403,
|
||||||
|
TOO_EARLY = 425,
|
||||||
}
|
}
|
||||||
export default HttpCodes;
|
export default HttpCodes;
|
||||||
|
@ -4,6 +4,11 @@ import BaseService from "@Services/BaseService";
|
|||||||
import { Customer } from "le-coffre-resources/dist/Notary";
|
import { Customer } from "le-coffre-resources/dist/Notary";
|
||||||
import { Service } from "typedi";
|
import { Service } from "typedi";
|
||||||
|
|
||||||
|
export class SmsNotExpiredError extends Error {
|
||||||
|
constructor() {
|
||||||
|
super("SMS code not expired");
|
||||||
|
}
|
||||||
|
}
|
||||||
@Service()
|
@Service()
|
||||||
export default class CustomersService extends BaseService {
|
export default class CustomersService extends BaseService {
|
||||||
constructor(private customerRepository: CustomersRepository) {
|
constructor(private customerRepository: CustomersRepository) {
|
||||||
@ -26,25 +31,58 @@ export default class CustomersService extends BaseService {
|
|||||||
return this.customerRepository.findOne(query);
|
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
|
* @description : Send SMS to verify the email of a customer (2FA)
|
||||||
* @throws {Error}
|
* 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) {
|
public async verifyEmail2FASms(email: string): Promise<Customer | null> {
|
||||||
const smsCode = Math.floor(100000 + Math.random() * 900000);
|
const customer = await this.getByEmail(email);
|
||||||
const now = new Date();
|
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(
|
return await this.customerRepository.update(
|
||||||
customer.uid as string,
|
customer.uid as string,
|
||||||
Customer.hydrate<Customer>({
|
Customer.hydrate<Customer>({
|
||||||
...customer,
|
...customer,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
smsCode: smsCode.toString(),
|
smsCode: totpPin.toString(),
|
||||||
smsCodeExpire: new Date(now.getTime() + 5 * 60000),
|
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
|
* @description : Set password for a customer
|
||||||
* @throws {Error} If customer cannot be updated
|
* @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