Uecko_ERP/apps/server/src/contexts/auth/application/auth.service.ts

243 lines
7.1 KiB
TypeScript
Raw Normal View History

2025-02-01 21:48:13 +00:00
import { Result, UniqueID } from "@common/domain";
import { ITransactionManager } from "@common/infrastructure/database";
2025-02-06 11:55:47 +00:00
import jwt from "jsonwebtoken";
2025-02-01 21:48:13 +00:00
import {
2025-02-03 13:12:36 +00:00
AuthenticatedUser,
2025-02-01 21:48:13 +00:00
EmailAddress,
2025-02-05 20:40:59 +00:00
HashPassword,
2025-02-01 21:48:13 +00:00
IAuthenticatedUserRepository,
2025-02-06 11:55:47 +00:00
PlainPassword,
2025-02-04 18:25:10 +00:00
TabContext,
2025-02-01 21:48:13 +00:00
Username,
2025-02-04 18:25:10 +00:00
} from "../domain";
import { ITabContextRepository } from "../domain/repositories/tab-context-repository.interface";
2025-02-01 21:48:13 +00:00
import { IAuthService } from "./auth-service.interface";
2025-02-06 11:55:47 +00:00
const SECRET_KEY = process.env.JWT_SECRET || "supersecretkey";
const ACCESS_EXPIRATION = process.env.JWT_ACCESS_EXPIRATION || "1h";
const REFRESH_EXPIRATION = process.env.JWT_REFRESH_EXPIRATION || "7d";
2025-02-01 21:48:13 +00:00
export class AuthService implements IAuthService {
2025-02-04 18:25:10 +00:00
private readonly _userRepo!: IAuthenticatedUserRepository;
private readonly _tabContactRepo!: ITabContextRepository;
2025-02-01 21:48:13 +00:00
private readonly _transactionManager!: ITransactionManager;
2025-02-03 13:12:36 +00:00
constructor(
2025-02-04 18:25:10 +00:00
userRepo: IAuthenticatedUserRepository,
tabContextRepo: ITabContextRepository,
2025-02-06 11:55:47 +00:00
transactionManager: ITransactionManager
2025-02-03 13:12:36 +00:00
) {
2025-02-04 18:25:10 +00:00
this._userRepo = userRepo;
this._tabContactRepo = tabContextRepo;
2025-02-01 21:48:13 +00:00
this._transactionManager = transactionManager;
2025-02-06 11:55:47 +00:00
}
generateAccessToken(payload: object): string {
return jwt.sign(payload, SECRET_KEY, { expiresIn: ACCESS_EXPIRATION });
}
generateRefreshToken(payload: object): string {
return jwt.sign(payload, SECRET_KEY, { expiresIn: REFRESH_EXPIRATION });
}
verifyToken(token: string): any {
return jwt.verify(token, SECRET_KEY);
2025-02-01 21:48:13 +00:00
}
2025-01-30 10:45:31 +00:00
/**
2025-02-03 21:54:51 +00:00
*
2025-01-30 10:45:31 +00:00
* Registra un nuevo usuario en la base de datos bajo transacción.
*/
2025-02-01 21:48:13 +00:00
async registerUser(params: {
username: Username;
email: EmailAddress;
2025-02-05 20:40:59 +00:00
hashPassword: HashPassword;
2025-02-03 13:12:36 +00:00
}): Promise<Result<AuthenticatedUser, Error>> {
try {
return await this._transactionManager.complete(async (transaction) => {
2025-02-05 20:40:59 +00:00
const { username, email, hashPassword } = params;
2025-01-30 10:45:31 +00:00
2025-02-03 13:12:36 +00:00
// Verificar si el usuario ya existe
2025-02-05 20:40:59 +00:00
const userExists = await this._userRepo.userExists(email, transaction);
2025-02-03 13:12:36 +00:00
if (userExists.isSuccess && userExists.data) {
return Result.fail(new Error("Email is already registered"));
}
2025-01-30 10:45:31 +00:00
2025-02-03 13:12:36 +00:00
const newUserId = UniqueID.generateNewID().data;
const userOrError = AuthenticatedUser.create(
{
username,
email,
2025-02-05 20:40:59 +00:00
hashPassword,
2025-02-03 13:12:36 +00:00
roles: ["USER"],
},
newUserId
);
if (userOrError.isFailure) {
return Result.fail(userOrError.error);
}
2025-02-04 18:25:10 +00:00
const createdResult = await this._userRepo.createUser(userOrError.data, transaction);
2025-02-03 13:12:36 +00:00
if (createdResult.isFailure) {
return Result.fail(createdResult.error);
}
return Result.ok(userOrError.data);
});
} catch (error: unknown) {
return Result.fail(error as Error);
}
2025-01-30 10:45:31 +00:00
}
/**
2025-02-03 21:54:51 +00:00
*
* Autentica a un usuario validando su email y contraseña.
2025-01-30 10:45:31 +00:00
*/
2025-02-03 21:54:51 +00:00
async loginUser(params: {
email: EmailAddress;
2025-02-05 20:40:59 +00:00
plainPassword: HashPassword;
2025-02-04 18:25:10 +00:00
tabId: UniqueID;
}): Promise<
Result<
{
user: AuthenticatedUser;
tokens: {
accessToken: string;
refreshToken: string;
};
},
Error
>
> {
2025-02-03 21:54:51 +00:00
try {
return await this._transactionManager.complete(async (transaction) => {
2025-02-05 20:40:59 +00:00
const { email, plainPassword, tabId } = params;
2025-02-04 18:25:10 +00:00
// Verificar que el tab ID está definido
if (!tabId.isDefined()) {
return Result.fail(new Error("Invalid tab id"));
}
2025-02-03 21:54:51 +00:00
// 🔹 Verificar si el usuario existe en la base de datos
2025-02-05 20:40:59 +00:00
const userResult = await this._userRepo.getUserByEmail(email, transaction);
2025-02-03 21:54:51 +00:00
if (userResult.isFailure) {
return Result.fail(new Error("Invalid email or password"));
}
const user = userResult.data;
// 🔹 Verificar que la contraseña sea correcta
2025-02-05 20:40:59 +00:00
const isValidPassword = await user.verifyPassword(plainPassword);
2025-02-03 21:54:51 +00:00
if (!isValidPassword) {
return Result.fail(new Error("Invalid email or password"));
}
2025-02-04 18:25:10 +00:00
// Registrar o actualizar el contexto de ese tab ID
const contextOrError = TabContext.create({
userId: user.id,
tabId: tabId,
});
if (contextOrError.isFailure) {
return Result.fail(new Error("Error creating user context"));
}
2025-02-06 11:55:47 +00:00
await this._tabContactRepo.registerContextByTabId(contextOrError.data, transaction);
2025-02-04 18:25:10 +00:00
2025-02-03 21:54:51 +00:00
// 🔹 Generar Access Token y Refresh Token
2025-02-06 11:55:47 +00:00
const accessToken = this.generateAccessToken({
2025-02-03 21:54:51 +00:00
userId: user.id.toString(),
email: email.toString(),
2025-02-04 18:25:10 +00:00
tabId: tabId.toString(),
2025-02-03 21:54:51 +00:00
roles: ["USER"],
});
2025-02-06 11:55:47 +00:00
const refreshToken = this.generateRefreshToken({
2025-02-03 21:54:51 +00:00
userId: user.id.toString(),
});
2025-02-04 18:25:10 +00:00
return Result.ok({
user,
tokens: {
accessToken,
refreshToken,
},
});
2025-02-03 21:54:51 +00:00
});
} catch (error: unknown) {
return Result.fail(error as Error);
}
}
2025-02-06 11:55:47 +00:00
/**
*
* Autentica a un usuario validando su email y contraseña.
*/
async logoutUser(params: { email: EmailAddress; tabId: UniqueID }): Promise<Result<void, Error>> {
try {
return await this._transactionManager.complete(async (transaction) => {
const { email, tabId } = params;
// Verificar que el tab ID está definido
if (!tabId.isDefined()) {
return Result.fail(new Error("Invalid tab id"));
}
// 🔹 Verificar si el usuario existe en la base de datos
const userResult = await this._userRepo.getUserByEmail(email, transaction);
if (userResult.isFailure) {
return Result.fail(new Error("Invalid email or password"));
}
const user = userResult.data;
const contextOrError = TabContext.create({
userId: user.id,
tabId: tabId,
});
if (contextOrError.isFailure) {
return Result.fail(new Error("Error creating user context"));
}
// Desregistrar el contexto de ese tab ID
await this._tabContactRepo.unregisterContextByTabId(contextOrError.data, transaction);
return Result.ok();
});
} catch (error: unknown) {
return Result.fail(error as Error);
}
}
async verifyUser(params: {
email: EmailAddress;
plainPassword: PlainPassword;
}): Promise<Result<AuthenticatedUser, Error>> {
try {
return await this._transactionManager.complete(async (transaction) => {
const { email, plainPassword } = params;
const userResult = await this._userRepo.getUserByEmail(email, transaction);
if (userResult.isFailure || !userResult.data) {
return Result.fail(new Error("Invalid email or password"));
}
const user = userResult.data;
const isValidPassword = await user.verifyPassword(plainPassword);
if (!isValidPassword) {
return Result.fail(new Error("Invalid email or password"));
}
return Result.ok(user);
});
} catch (error: unknown) {
return Result.fail(error as Error);
}
}
2025-01-30 10:45:31 +00:00
}