Refacto login

This commit is contained in:
Maxime Lalo 2023-11-24 14:14:19 +01:00
parent fc8b86b7ca
commit d3f9527f85
2 changed files with 90 additions and 50 deletions

View File

@ -3,7 +3,13 @@ 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, { SmsNotExpiredError } from "@Services/customer/CustomersService/CustomersService"; import CustomersService, {
InvalidPasswordError,
InvalidTotpCodeError,
NotRegisteredCustomerError,
SmsNotExpiredError,
TotpCodeExpiredError,
} 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";
@ -28,7 +34,7 @@ export default class AuthController extends ApiController {
this.httpNotFoundRequest(response, "Customer not found"); this.httpNotFoundRequest(response, "Customer not found");
return; return;
} }
this.httpSuccess(response, { partialPhoneNumber: customer.contact?.cell_phone_number.slice(-4) }); this.httpSuccess(response, { partialPhoneNumber: customer.contact?.cell_phone_number.replace(/\s/g, "").slice(-4) });
} catch (error) { } catch (error) {
if (error instanceof SmsNotExpiredError) { if (error instanceof SmsNotExpiredError) {
this.httpTooEarlyRequest(response, error.message); this.httpTooEarlyRequest(response, error.message);
@ -60,56 +66,28 @@ export default class AuthController extends ApiController {
return; return;
} }
let customer = await this.customerService.getOne({ try {
where: { const customer = await this.customerService.login(email, smsCode, password);
contact: {
email,
},
},
include: {
contact: true,
},
});
if (!customer) { if (!customer) {
this.httpNotFoundRequest(response, "Customer not found"); this.httpBadRequest(response, "Customer not found");
return; return;
} }
if (!customer.smsCode) {
this.httpBadRequest(response, "No sms code found");
return;
}
if (!customer.smsCodeExpire || new Date().getTime() > customer.smsCodeExpire.getTime()) {
this.httpBadRequest(response, "Sms code expired");
return;
}
if (customer.smsCode !== smsCode) {
this.httpBadRequest(response, "Invalid sms code");
return;
}
if (!customer.password) {
this.httpBadRequest(response, "Customer not registered");
return;
}
// compare password to the hash
const isPasswordValid = await this.authService.comparePassword(password, customer.password);
if (!isPasswordValid) {
this.httpBadRequest(response, "Invalid password");
return;
}
const customerHydrated = Customer.hydrate<Customer>(customer); const customerHydrated = Customer.hydrate<Customer>(customer);
const payload = await this.authService.getCustomerJwtPayload([customerHydrated]); const payload = await this.authService.getCustomerJwtPayload([customerHydrated]);
const accessToken = this.authService.generateAccessToken(payload); const accessToken = this.authService.generateAccessToken(payload);
const refreshToken = this.authService.generateRefreshToken(payload); const refreshToken = this.authService.generateRefreshToken(payload);
try {
this.httpSuccess(response, { accessToken, refreshToken }); this.httpSuccess(response, { accessToken, refreshToken });
} catch (error) { } catch (error) {
if (error instanceof TotpCodeExpiredError || error instanceof NotRegisteredCustomerError) {
this.httpBadRequest(response, error.message);
return;
}
if (error instanceof InvalidTotpCodeError || error instanceof InvalidPasswordError) {
this.httpUnauthorized(response, error.message);
return;
}
console.log(error); console.log(error);
this.httpInternalError(response); this.httpInternalError(response);
return; return;

View File

@ -1,6 +1,7 @@
import { Customers, Prisma } from "@prisma/client"; import { Customers, Prisma } from "@prisma/client";
import CustomersRepository from "@Repositories/CustomersRepository"; import CustomersRepository from "@Repositories/CustomersRepository";
import BaseService from "@Services/BaseService"; import BaseService from "@Services/BaseService";
import AuthService from "@Services/common/AuthService/AuthService";
import { Customer } from "le-coffre-resources/dist/Notary"; import { Customer } from "le-coffre-resources/dist/Notary";
import { Service } from "typedi"; import { Service } from "typedi";
@ -9,9 +10,33 @@ export class SmsNotExpiredError extends Error {
super("SMS code not expired"); super("SMS code not expired");
} }
} }
export class TotpCodeExpiredError extends Error {
constructor() {
super("Totp code not found or expired");
}
}
export class InvalidTotpCodeError extends Error {
constructor() {
super("Invalid Totp code");
}
}
export class NotRegisteredCustomerError extends Error {
constructor() {
super("Customer not registered");
}
}
export class InvalidPasswordError extends Error {
constructor() {
super("Invalid password");
}
}
@Service() @Service()
export default class CustomersService extends BaseService { export default class CustomersService extends BaseService {
constructor(private customerRepository: CustomersRepository) { constructor(private customerRepository: CustomersRepository, private authService: AuthService) {
super(); super();
} }
@ -111,4 +136,41 @@ export default class CustomersService extends BaseService {
}, },
}); });
} }
/**
*
* @description : Login a customer
* 1: Check if the customer exists
* 2: Check if the SMS code is existing and is not expired
* 3: Check if the SMS code is valid
* 4: Check if the user has a password or it's their first login
* 5: Check if the password is valid
* 6: Return the customer
* @param email
* @param smsCode
* @param password
* @returns Customer | null
*/
public async login(email: string, smsCode: string, password: string): Promise<Customer | null> {
// 1: Check if the customer exists
const customer = await this.getByEmail(email);
if (!customer) return null;
// 2: Check if the SMS code is existing and is not expired
if (!customer.smsCode || !customer.smsCodeExpire || new Date().getTime() > customer.smsCodeExpire.getTime())
throw new TotpCodeExpiredError();
// 3: Check if the SMS code is valid
if (customer.smsCode !== smsCode) throw new InvalidTotpCodeError();
// 4: Check if the user has a password or it's their first login
if (!customer.password) throw new NotRegisteredCustomerError();
// 5: Check if the password is valid
const isPasswordValid = await this.authService.comparePassword(password, customer.password);
if (!isPasswordValid) throw new InvalidPasswordError();
// 6: Return the customer
return customer;
}
} }