.
This commit is contained in:
parent
36788f34e8
commit
e8d606714a
@ -9,6 +9,11 @@
|
|||||||
"date": 1738768744297,
|
"date": 1738768744297,
|
||||||
"name": "debug-2025-02-05.log",
|
"name": "debug-2025-02-05.log",
|
||||||
"hash": "35182b14bda063a4b734238473f84cfa66a0362a76d8f12f0c20277df81c7256"
|
"hash": "35182b14bda063a4b734238473f84cfa66a0362a76d8f12f0c20277df81c7256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": 1738834648631,
|
||||||
|
"name": "debug-2025-02-06.log",
|
||||||
|
"hash": "fbca91e028d38f5065d0e5bdbbfdbf49aadd06feddd074c6a1419b7c9e6b4fd7"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hashType": "sha256"
|
"hashType": "sha256"
|
||||||
|
|||||||
@ -9,6 +9,11 @@
|
|||||||
"date": 1738768744292,
|
"date": 1738768744292,
|
||||||
"name": "error-2025-02-05.log",
|
"name": "error-2025-02-05.log",
|
||||||
"hash": "c32d976d68382b2ba2ddec8c907c30547ec9fda2bb31180bfdbeb685964810a8"
|
"hash": "c32d976d68382b2ba2ddec8c907c30547ec9fda2bb31180bfdbeb685964810a8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": 1738834648629,
|
||||||
|
"name": "error-2025-02-06.log",
|
||||||
|
"hash": "17c45ac8b0d8d1bf22f601247a7f1384f004d06420d39e78eb65cb0a26815e13"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hashType": "sha256"
|
"hashType": "sha256"
|
||||||
|
|||||||
@ -14,6 +14,7 @@ const initLogger = () => {
|
|||||||
format.timestamp(),
|
format.timestamp(),
|
||||||
format.align(),
|
format.align(),
|
||||||
format.splat(),
|
format.splat(),
|
||||||
|
format.errors({ stack: !isProduction }),
|
||||||
format.printf((info) => {
|
format.printf((info) => {
|
||||||
const rid = rTracer.id();
|
const rid = rTracer.id();
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
import { AuthenticatedUser } from "../domain";
|
|
||||||
|
|
||||||
export interface IAuthProvider {
|
|
||||||
/* JWT Strategy */
|
|
||||||
generateAccessToken(payload: object): string;
|
|
||||||
generateRefreshToken(payload: object): string;
|
|
||||||
verifyToken(token: string): Promise<AuthenticatedUser | null>;
|
|
||||||
|
|
||||||
/* LocalStrategy */
|
|
||||||
//_verifyUser(email: string, password: string): Promise<AuthenticatedUser | null>;
|
|
||||||
}
|
|
||||||
@ -2,6 +2,10 @@ import { Result, UniqueID } from "@common/domain";
|
|||||||
import { AuthenticatedUser, EmailAddress, HashPassword, PlainPassword, Username } from "../domain";
|
import { AuthenticatedUser, EmailAddress, HashPassword, PlainPassword, Username } from "../domain";
|
||||||
|
|
||||||
export interface IAuthService {
|
export interface IAuthService {
|
||||||
|
generateAccessToken(payload: object): string;
|
||||||
|
generateRefreshToken(payload: object): string;
|
||||||
|
verifyToken(token: string): Promise<AuthenticatedUser | null>;
|
||||||
|
|
||||||
registerUser(params: {
|
registerUser(params: {
|
||||||
username: Username;
|
username: Username;
|
||||||
email: EmailAddress;
|
email: EmailAddress;
|
||||||
@ -24,4 +28,11 @@ export interface IAuthService {
|
|||||||
Error
|
Error
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
logoutUser(params: { email: EmailAddress; tabId: UniqueID }): Promise<Result<void, Error>>;
|
||||||
|
|
||||||
|
verifyUser(params: {
|
||||||
|
email: EmailAddress;
|
||||||
|
plainPassword: PlainPassword;
|
||||||
|
}): Promise<Result<AuthenticatedUser, Error>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,33 +1,47 @@
|
|||||||
import { Result, UniqueID } from "@common/domain";
|
import { Result, UniqueID } from "@common/domain";
|
||||||
import { ITransactionManager } from "@common/infrastructure/database";
|
import { ITransactionManager } from "@common/infrastructure/database";
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
import {
|
import {
|
||||||
AuthenticatedUser,
|
AuthenticatedUser,
|
||||||
EmailAddress,
|
EmailAddress,
|
||||||
HashPassword,
|
HashPassword,
|
||||||
IAuthenticatedUserRepository,
|
IAuthenticatedUserRepository,
|
||||||
|
PlainPassword,
|
||||||
TabContext,
|
TabContext,
|
||||||
Username,
|
Username,
|
||||||
} from "../domain";
|
} from "../domain";
|
||||||
import { ITabContextRepository } from "../domain/repositories/tab-context-repository.interface";
|
import { ITabContextRepository } from "../domain/repositories/tab-context-repository.interface";
|
||||||
import { IAuthProvider } from "./auth-provider.interface";
|
|
||||||
import { IAuthService } from "./auth-service.interface";
|
import { IAuthService } from "./auth-service.interface";
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
export class AuthService implements IAuthService {
|
export class AuthService implements IAuthService {
|
||||||
private readonly _userRepo!: IAuthenticatedUserRepository;
|
private readonly _userRepo!: IAuthenticatedUserRepository;
|
||||||
private readonly _tabContactRepo!: ITabContextRepository;
|
private readonly _tabContactRepo!: ITabContextRepository;
|
||||||
private readonly _transactionManager!: ITransactionManager;
|
private readonly _transactionManager!: ITransactionManager;
|
||||||
private readonly _authProvider: IAuthProvider;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
userRepo: IAuthenticatedUserRepository,
|
userRepo: IAuthenticatedUserRepository,
|
||||||
tabContextRepo: ITabContextRepository,
|
tabContextRepo: ITabContextRepository,
|
||||||
transactionManager: ITransactionManager,
|
transactionManager: ITransactionManager
|
||||||
authProvider: IAuthProvider
|
|
||||||
) {
|
) {
|
||||||
this._userRepo = userRepo;
|
this._userRepo = userRepo;
|
||||||
this._tabContactRepo = tabContextRepo;
|
this._tabContactRepo = tabContextRepo;
|
||||||
this._transactionManager = transactionManager;
|
this._transactionManager = transactionManager;
|
||||||
this._authProvider = authProvider;
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -125,25 +139,23 @@ export class AuthService implements IAuthService {
|
|||||||
const contextOrError = TabContext.create({
|
const contextOrError = TabContext.create({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
tabId: tabId,
|
tabId: tabId,
|
||||||
companyId: UniqueID.generateUndefinedID().data,
|
|
||||||
branchId: UniqueID.generateUndefinedID().data,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (contextOrError.isFailure) {
|
if (contextOrError.isFailure) {
|
||||||
return Result.fail(new Error("Error creating user context"));
|
return Result.fail(new Error("Error creating user context"));
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._tabContactRepo.registerContext(contextOrError.data, transaction);
|
await this._tabContactRepo.registerContextByTabId(contextOrError.data, transaction);
|
||||||
|
|
||||||
// 🔹 Generar Access Token y Refresh Token
|
// 🔹 Generar Access Token y Refresh Token
|
||||||
const accessToken = this._authProvider.generateAccessToken({
|
const accessToken = this.generateAccessToken({
|
||||||
userId: user.id.toString(),
|
userId: user.id.toString(),
|
||||||
email: email.toString(),
|
email: email.toString(),
|
||||||
tabId: tabId.toString(),
|
tabId: tabId.toString(),
|
||||||
roles: ["USER"],
|
roles: ["USER"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const refreshToken = this._authProvider.generateRefreshToken({
|
const refreshToken = this.generateRefreshToken({
|
||||||
userId: user.id.toString(),
|
userId: user.id.toString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -159,4 +171,72 @@ export class AuthService implements IAuthService {
|
|||||||
return Result.fail(error as Error);
|
return Result.fail(error as Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,11 @@
|
|||||||
import { createSequelizeTransactionManager } from "@common/infrastructure";
|
import { createSequelizeTransactionManager } from "@common/infrastructure";
|
||||||
|
|
||||||
import { createAuthenticatedUserRepository, createTabContextRepository } from "../infraestructure";
|
import { createAuthenticatedUserRepository, createTabContextRepository } from "../infraestructure";
|
||||||
import { createPassportAuthProvider } from "../infraestructure/passport/passport-auth-provider";
|
|
||||||
import { IAuthProvider } from "./auth-provider.interface";
|
|
||||||
import { IAuthService } from "./auth-service.interface";
|
import { IAuthService } from "./auth-service.interface";
|
||||||
import { AuthService } from "./auth.service";
|
import { AuthService } from "./auth.service";
|
||||||
import { ITabContextService } from "./tab-context-service.interface";
|
import { ITabContextService } from "./tab-context-service.interface";
|
||||||
import { TabContextService } from "./tab-context.service";
|
import { TabContextService } from "./tab-context.service";
|
||||||
|
|
||||||
export * from "./auth-provider.interface";
|
|
||||||
export * from "./auth-service.interface";
|
export * from "./auth-service.interface";
|
||||||
|
|
||||||
export const createAuthService = (): IAuthService => {
|
export const createAuthService = (): IAuthService => {
|
||||||
@ -16,16 +13,7 @@ export const createAuthService = (): IAuthService => {
|
|||||||
const authenticatedUserRepository = createAuthenticatedUserRepository();
|
const authenticatedUserRepository = createAuthenticatedUserRepository();
|
||||||
const tabContextRepository = createTabContextRepository();
|
const tabContextRepository = createTabContextRepository();
|
||||||
|
|
||||||
const authProvider: IAuthProvider = createPassportAuthProvider(
|
return new AuthService(authenticatedUserRepository, tabContextRepository, transactionManager);
|
||||||
authenticatedUserRepository,
|
|
||||||
transactionManager
|
|
||||||
);
|
|
||||||
return new AuthService(
|
|
||||||
authenticatedUserRepository,
|
|
||||||
tabContextRepository,
|
|
||||||
transactionManager,
|
|
||||||
authProvider
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createTabContextService = (): ITabContextService => {
|
export const createTabContextService = (): ITabContextService => {
|
||||||
|
|||||||
@ -3,12 +3,6 @@ import { TabContext } from "../domain";
|
|||||||
|
|
||||||
export interface ITabContextService {
|
export interface ITabContextService {
|
||||||
getContextByTabId(tabId: UniqueID): Promise<Result<TabContext, Error>>;
|
getContextByTabId(tabId: UniqueID): Promise<Result<TabContext, Error>>;
|
||||||
createContext(params: {
|
createContext(params: { tabId: UniqueID; userId: UniqueID }): Promise<Result<TabContext, Error>>;
|
||||||
tabId: UniqueID;
|
removeContext(params: { tabId: UniqueID; userId: UniqueID }): Promise<Result<void, Error>>;
|
||||||
userId: UniqueID;
|
|
||||||
companyId: UniqueID;
|
|
||||||
branchId: UniqueID;
|
|
||||||
}): Promise<Result<TabContext, Error>>;
|
|
||||||
assignCompany(tabId: UniqueID, companyId: UniqueID): Promise<Result<void, Error>>;
|
|
||||||
removeContext(tabId: UniqueID): Promise<Result<void, Error>>;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,10 +42,8 @@ export class TabContextService implements ITabContextService {
|
|||||||
async createContext(params: {
|
async createContext(params: {
|
||||||
tabId: UniqueID;
|
tabId: UniqueID;
|
||||||
userId: UniqueID;
|
userId: UniqueID;
|
||||||
companyId: UniqueID;
|
|
||||||
branchId: UniqueID;
|
|
||||||
}): Promise<Result<TabContext, Error>> {
|
}): Promise<Result<TabContext, Error>> {
|
||||||
const { tabId, userId, companyId, branchId } = params;
|
const { tabId, userId } = params;
|
||||||
|
|
||||||
if (!userId || !tabId) {
|
if (!userId || !tabId) {
|
||||||
return Result.fail(new Error("User ID and Tab ID are required"));
|
return Result.fail(new Error("User ID and Tab ID are required"));
|
||||||
@ -57,8 +55,6 @@ export class TabContextService implements ITabContextService {
|
|||||||
{
|
{
|
||||||
userId,
|
userId,
|
||||||
tabId,
|
tabId,
|
||||||
companyId,
|
|
||||||
branchId,
|
|
||||||
},
|
},
|
||||||
UniqueID.generateNewID().data
|
UniqueID.generateNewID().data
|
||||||
);
|
);
|
||||||
@ -67,7 +63,7 @@ export class TabContextService implements ITabContextService {
|
|||||||
return Result.fail(contextOrError.error);
|
return Result.fail(contextOrError.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._respository.registerContext(contextOrError.data, transaction);
|
await this._respository.registerContextByTabId(contextOrError.data, transaction);
|
||||||
|
|
||||||
return Result.ok(contextOrError.data);
|
return Result.ok(contextOrError.data);
|
||||||
});
|
});
|
||||||
@ -76,39 +72,31 @@ export class TabContextService implements ITabContextService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Asigna una empresa activa a un contexto de pestaña
|
|
||||||
*/
|
|
||||||
async assignCompany(tabId: UniqueID, companyId: UniqueID): Promise<Result<void, Error>> {
|
|
||||||
if (!companyId || !tabId) {
|
|
||||||
return Result.fail(new Error("Tab ID and Company ID are required"));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return await this._transactionManager.complete(async (transaction) => {
|
|
||||||
// Verificar si la pestaña existe
|
|
||||||
const tabContextOrError = await this._respository.contextExists(tabId, transaction);
|
|
||||||
if (tabContextOrError.isFailure || !tabContextOrError.data) {
|
|
||||||
return Result.fail(new Error("Invalid or expired Tab ID"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return await this._respository.updateCompanyByTabId(tabId, companyId, transaction);
|
|
||||||
});
|
|
||||||
} catch (error: unknown) {
|
|
||||||
return Result.fail(error as Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Elimina un contexto de pestaña por su ID
|
* Elimina un contexto de pestaña por su ID
|
||||||
*/
|
*/
|
||||||
async removeContext(tabId: UniqueID): Promise<Result<void, Error>> {
|
async removeContext(params: { tabId: UniqueID; userId: UniqueID }): Promise<Result<void, Error>> {
|
||||||
if (!tabId) {
|
const { tabId, userId } = params;
|
||||||
return Result.fail(new Error("Tab ID is required"));
|
|
||||||
|
if (!userId || !tabId) {
|
||||||
|
return Result.fail(new Error("User ID and Tab ID are required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await this._transactionManager.complete(async (transaction) => {
|
return await this._transactionManager.complete(async (transaction) => {
|
||||||
return await this._respository.deleteContextByTabId(tabId, transaction);
|
const contextOrError = TabContext.create(
|
||||||
|
{
|
||||||
|
userId,
|
||||||
|
tabId,
|
||||||
|
},
|
||||||
|
UniqueID.generateNewID().data
|
||||||
|
);
|
||||||
|
|
||||||
|
if (contextOrError.isFailure) {
|
||||||
|
return Result.fail(contextOrError.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this._respository.unregisterContextByTabId(contextOrError.data, transaction);
|
||||||
});
|
});
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
return Result.fail(error as Error);
|
return Result.fail(error as Error);
|
||||||
|
|||||||
@ -3,18 +3,11 @@ import { DomainEntity, Result, UniqueID } from "@common/domain";
|
|||||||
export interface ITabContextProps {
|
export interface ITabContextProps {
|
||||||
tabId: UniqueID;
|
tabId: UniqueID;
|
||||||
userId: UniqueID;
|
userId: UniqueID;
|
||||||
companyId: UniqueID;
|
|
||||||
branchId: UniqueID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITabContext {
|
export interface ITabContext {
|
||||||
tabId: UniqueID;
|
tabId: UniqueID;
|
||||||
userId: UniqueID;
|
userId: UniqueID;
|
||||||
companyId: UniqueID;
|
|
||||||
branchId: UniqueID;
|
|
||||||
|
|
||||||
hasCompanyAssigned(): boolean;
|
|
||||||
hasBranchAssigned(): boolean;
|
|
||||||
|
|
||||||
toPersistenceData(): any;
|
toPersistenceData(): any;
|
||||||
}
|
}
|
||||||
@ -32,22 +25,6 @@ export class TabContext extends DomainEntity<ITabContextProps> implements ITabCo
|
|||||||
return this._props.userId;
|
return this._props.userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
get companyId(): UniqueID {
|
|
||||||
return this._props.companyId;
|
|
||||||
}
|
|
||||||
|
|
||||||
get branchId(): UniqueID {
|
|
||||||
return this._props.branchId;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasCompanyAssigned(): boolean {
|
|
||||||
return this._props.companyId.isDefined();
|
|
||||||
}
|
|
||||||
|
|
||||||
hasBranchAssigned(): boolean {
|
|
||||||
return this._props.branchId.isDefined();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔹 Devuelve una representación lista para persistencia
|
* 🔹 Devuelve una representación lista para persistencia
|
||||||
*/
|
*/
|
||||||
@ -56,8 +33,6 @@ export class TabContext extends DomainEntity<ITabContextProps> implements ITabCo
|
|||||||
id: this._id.toString(),
|
id: this._id.toString(),
|
||||||
tab_id: this.tabId.toString(),
|
tab_id: this.tabId.toString(),
|
||||||
user_id: this.userId.toString(),
|
user_id: this.userId.toString(),
|
||||||
company_id: this.companyId.toString(),
|
|
||||||
branch_id: this.branchId.toString(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,12 +4,16 @@ import { TabContext } from "../entities";
|
|||||||
|
|
||||||
export interface ITabContextRepository {
|
export interface ITabContextRepository {
|
||||||
getContextByTabId(tabId: UniqueID, transaction?: any): Promise<Result<TabContext, Error>>;
|
getContextByTabId(tabId: UniqueID, transaction?: any): Promise<Result<TabContext, Error>>;
|
||||||
registerContext(context: TabContext, transaction?: Transaction): Promise<Result<void, Error>>;
|
|
||||||
contextExists(tabId: UniqueID, transaction?: any): Promise<Result<boolean, Error>>;
|
contextExistsByTabId(tabId: UniqueID, transaction?: any): Promise<Result<boolean, Error>>;
|
||||||
updateCompanyByTabId(
|
|
||||||
tabId: UniqueID,
|
registerContextByTabId(
|
||||||
companyId: UniqueID,
|
context: TabContext,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<void, Error>>;
|
||||||
|
|
||||||
|
unregisterContextByTabId(
|
||||||
|
context: TabContext,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
): Promise<Result<void, Error>>;
|
): Promise<Result<void, Error>>;
|
||||||
deleteContextByTabId(tabId: UniqueID, transaction?: any): Promise<Result<void, Error>>;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,9 @@
|
|||||||
export * from "./passport-auth-provider";
|
import { createAuthService } from "@contexts/auth/application";
|
||||||
|
import { PassportAuthProvider } from "./passport-auth-provider";
|
||||||
|
|
||||||
|
export const createPassportAuthProvider = () => {
|
||||||
|
const _authService = createAuthService();
|
||||||
|
return new PassportAuthProvider(_authService);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initializePassportAuthProvide = () => createPassportAuthProvider();
|
||||||
|
|||||||
@ -1,38 +1,32 @@
|
|||||||
import { createSequelizeTransactionManager } from "@common/infrastructure";
|
import { IAuthService } from "@contexts/auth/application";
|
||||||
import { ITransactionManager } from "@common/infrastructure/database";
|
import { AuthenticatedUser, EmailAddress, PlainPassword } from "@contexts/auth/domain";
|
||||||
import { IAuthProvider } from "@contexts/auth/application";
|
|
||||||
import {
|
|
||||||
AuthenticatedUser,
|
|
||||||
EmailAddress,
|
|
||||||
IAuthenticatedUserRepository,
|
|
||||||
PlainPassword,
|
|
||||||
} from "@contexts/auth/domain";
|
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
import passport from "passport";
|
import passport from "passport";
|
||||||
import { ExtractJwt, Strategy as JwtStrategy } from "passport-jwt";
|
import { ExtractJwt, Strategy as JwtStrategy } from "passport-jwt";
|
||||||
import { Strategy as LocalStrategy } from "passport-local";
|
import { Strategy as LocalStrategy } from "passport-local";
|
||||||
import { createAuthenticatedUserRepository } from "../sequelize";
|
|
||||||
|
|
||||||
const SECRET_KEY = process.env.JWT_SECRET || "supersecretkey";
|
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";
|
|
||||||
|
|
||||||
export class PassportAuthProvider implements IAuthProvider {
|
export class PassportAuthProvider {
|
||||||
private readonly _repository: IAuthenticatedUserRepository;
|
private readonly _authService: IAuthService;
|
||||||
private readonly _transactionManager!: ITransactionManager;
|
|
||||||
|
|
||||||
private async _verifyUser(email: string, password: string): Promise<AuthenticatedUser | null> {
|
private async _verifyUser(email: string, password: string): Promise<AuthenticatedUser | null> {
|
||||||
const emailVO = EmailAddress.create(email);
|
const emailVO = EmailAddress.create(email);
|
||||||
if (emailVO.isFailure) return Promise.resolve(null);
|
if (emailVO.isFailure) return Promise.resolve(null);
|
||||||
|
|
||||||
const passwordVO = PlainPassword.create(password);
|
const plainPasswordVO = PlainPassword.create(password);
|
||||||
if (passwordVO.isFailure) return Promise.resolve(null);
|
if (plainPasswordVO.isFailure) return Promise.resolve(null);
|
||||||
|
|
||||||
const userResult = await this._repository.getUserByEmail(emailVO.data);
|
const userResult = await this._authService.verifyUser({
|
||||||
if (userResult.isFailure || !userResult.data) return Promise.resolve(null);
|
email: emailVO.data,
|
||||||
|
plainPassword: plainPasswordVO.data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userResult.isFailure || !userResult.data) {
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
const user = userResult.data;
|
const user = userResult.data;
|
||||||
const isValidPassword = await user.verifyPassword(passwordVO.data);
|
const isValidPassword = await user.verifyPassword(plainPasswordVO.data);
|
||||||
|
|
||||||
return !isValidPassword ? Promise.resolve(null) : Promise.resolve(user);
|
return !isValidPassword ? Promise.resolve(null) : Promise.resolve(user);
|
||||||
}
|
}
|
||||||
@ -50,6 +44,7 @@ export class PassportAuthProvider implements IAuthProvider {
|
|||||||
"jwt",
|
"jwt",
|
||||||
new JwtStrategy(jwtOptions, (tokenPayload, done) => {
|
new JwtStrategy(jwtOptions, (tokenPayload, done) => {
|
||||||
try {
|
try {
|
||||||
|
console.log(tokenPayload);
|
||||||
return done(null, tokenPayload);
|
return done(null, tokenPayload);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return done(error, false);
|
return done(error, false);
|
||||||
@ -77,32 +72,8 @@ export class PassportAuthProvider implements IAuthProvider {
|
|||||||
passport.initialize();
|
passport.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(repository: IAuthenticatedUserRepository, transactionManager: ITransactionManager) {
|
constructor(authService: IAuthService) {
|
||||||
this._repository = repository;
|
this._authService = authService;
|
||||||
this._transactionManager = transactionManager;
|
|
||||||
this.initializePassport();
|
this.initializePassport();
|
||||||
}
|
}
|
||||||
|
|
||||||
generateAccessToken(payload: object): string {
|
|
||||||
return jwt.sign(payload, SECRET_KEY, { expiresIn: String(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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createPassportAuthProvider = (
|
|
||||||
repository?: IAuthenticatedUserRepository,
|
|
||||||
transactionManager?: ITransactionManager
|
|
||||||
) => {
|
|
||||||
const _transactionManager = transactionManager || createSequelizeTransactionManager();
|
|
||||||
const _repository = repository || createAuthenticatedUserRepository();
|
|
||||||
return new PassportAuthProvider(_repository, _transactionManager);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const initializePassportAuthProvide = () => createPassportAuthProvider();
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { Result, UniqueID } from "@common/domain";
|
|||||||
import { SequelizeRepository } from "@common/infrastructure";
|
import { SequelizeRepository } from "@common/infrastructure";
|
||||||
import { TabContext } from "@contexts/auth/domain/";
|
import { TabContext } from "@contexts/auth/domain/";
|
||||||
import { ITabContextRepository } from "@contexts/auth/domain/repositories/tab-context-repository.interface";
|
import { ITabContextRepository } from "@contexts/auth/domain/repositories/tab-context-repository.interface";
|
||||||
import { Transaction } from "sequelize";
|
import { Op, Transaction } from "sequelize";
|
||||||
import { createTabContextMapper, ITabContextMapper } from "../mappers";
|
import { createTabContextMapper, ITabContextMapper } from "../mappers";
|
||||||
import { TabContextModel } from "./tab-context.model";
|
import { TabContextModel } from "./tab-context.model";
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ export class TabContextRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async contextExists(tabId: UniqueID, transaction?: any): Promise<Result<boolean, Error>> {
|
async contextExistsByTabId(tabId: UniqueID, transaction?: any): Promise<Result<boolean, Error>> {
|
||||||
try {
|
try {
|
||||||
const result: any = await this._exists(
|
const result: any = await this._exists(
|
||||||
TabContextModel,
|
TabContextModel,
|
||||||
@ -67,57 +67,57 @@ export class TabContextRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crea un contexto para un tab id o actualiza si ya existe
|
* Registra un contexto para un tab id o actualiza si ya existe
|
||||||
* @param context
|
* @param context
|
||||||
* @param transaction
|
* @param transaction
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async registerContext(
|
async registerContextByTabId(
|
||||||
context: TabContext,
|
context: TabContext,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
): Promise<Result<void, Error>> {
|
): Promise<Result<void, Error>> {
|
||||||
try {
|
try {
|
||||||
const { id } = context;
|
const { userId, tabId } = context;
|
||||||
const persistenceData = this._mapper.toPersistence(context);
|
const data = this._mapper.toPersistence(context);
|
||||||
|
|
||||||
|
// Si existe el contexto de ese tabId, lo actualizo.
|
||||||
|
|
||||||
|
if (await this._exists(TabContextModel, "tab_id", tabId.toString())) {
|
||||||
|
await TabContextModel.update(data, {
|
||||||
|
where: { [Op.and]: [{ tab_id: tabId.toString() }, { user_id: userId.toString() }] },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await TabContextModel.create(data, {
|
||||||
|
include: [{ all: true }],
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await this._save(TabContextModel, id, persistenceData, {}, transaction);
|
|
||||||
return Result.ok();
|
return Result.ok();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
return this._handleDatabaseError(error, this._customErrorMapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateCompanyByTabId(
|
/**
|
||||||
tabId: UniqueID,
|
* Desregistra un contexto para un tab id o actualiza si ya existe
|
||||||
companyId: UniqueID,
|
* @param context
|
||||||
|
* @param transaction
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
|
||||||
|
async unregisterContextByTabId(
|
||||||
|
context: TabContext,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
): Promise<Result<void, Error>> {
|
): Promise<Result<void, Error>> {
|
||||||
try {
|
try {
|
||||||
await TabContextModel.update(
|
const { userId, tabId } = context;
|
||||||
{ company_id: companyId.toString() },
|
|
||||||
{
|
|
||||||
where: {
|
|
||||||
tab_id: tabId.toString(),
|
|
||||||
},
|
|
||||||
transaction,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return Result.ok();
|
|
||||||
} catch (error: any) {
|
|
||||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteContextByTabId(tabId: UniqueID, transaction?: any): Promise<Result<void, Error>> {
|
|
||||||
try {
|
|
||||||
await TabContextModel.destroy({
|
await TabContextModel.destroy({
|
||||||
where: {
|
where: { [Op.and]: [{ tab_id: tabId.toString() }, { user_id: userId.toString() }] },
|
||||||
tab_id: tabId.toString(),
|
|
||||||
},
|
|
||||||
transaction,
|
transaction,
|
||||||
force: false,
|
|
||||||
});
|
});
|
||||||
return Result.ok();
|
return Result.ok();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { UniqueID } from "@common/domain";
|
import { Result, UniqueID } from "@common/domain";
|
||||||
import { ExpressController } from "@common/presentation";
|
import { ExpressController } from "@common/presentation";
|
||||||
import { createAuthService, IAuthService } from "@contexts/auth/application";
|
import { createAuthService, IAuthService } from "@contexts/auth/application";
|
||||||
|
import { EmailAddress } from "@contexts/auth/domain";
|
||||||
|
|
||||||
class LogoutController extends ExpressController {
|
class LogoutController extends ExpressController {
|
||||||
private readonly _authService!: IAuthService;
|
private readonly _authService!: IAuthService;
|
||||||
@ -12,22 +13,21 @@ class LogoutController extends ExpressController {
|
|||||||
|
|
||||||
async executeImpl() {
|
async executeImpl() {
|
||||||
const tabId = this.req.headers["x-tab-id"];
|
const tabId = this.req.headers["x-tab-id"];
|
||||||
|
|
||||||
|
const emailVO = EmailAddress.create(this.req.body.email);
|
||||||
const tabIdVO = UniqueID.create(String(tabId));
|
const tabIdVO = UniqueID.create(String(tabId));
|
||||||
|
|
||||||
if (tabIdVO.isFailure) {
|
const resultValidation = Result.combine([emailVO, tabIdVO]);
|
||||||
return this.clientError("Invalid tab id", [tabIdVO.error]);
|
|
||||||
|
if (resultValidation.isFailure) {
|
||||||
|
return this.clientError("Invalid input data", resultValidation.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userOrError = await this._authService.logoutUser({
|
await this._authService.logoutUser({
|
||||||
email: emailVO.data,
|
email: emailVO.data,
|
||||||
plainPassword: plainPasswordVO.data,
|
|
||||||
tabId: tabIdVO.data,
|
tabId: tabIdVO.data,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (userOrError.isFailure) {
|
|
||||||
return this.unauthorizedError(userOrError.error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.ok();
|
return this.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +1,10 @@
|
|||||||
import { validateRequest } from "@common/presentation";
|
import { validateRequest } from "@common/presentation";
|
||||||
import { validateTabContextHeader, validateUser } from "@contexts/auth/presentation";
|
import { validateTabContextHeader, validateUser } from "@contexts/auth/presentation";
|
||||||
import { createLoginController } from "@contexts/auth/presentation/controllers";
|
import { createLoginController } from "@contexts/auth/presentation/controllers";
|
||||||
|
import { createLogoutController } from "@contexts/auth/presentation/controllers/logout/logout.controller";
|
||||||
import { createRegisterController } from "@contexts/auth/presentation/controllers/register/register.controller";
|
import { createRegisterController } from "@contexts/auth/presentation/controllers/register/register.controller";
|
||||||
import { LoginUserSchema, RegisterUserSchema } from "@contexts/auth/presentation/dto";
|
import { LoginUserSchema, RegisterUserSchema } from "@contexts/auth/presentation/dto";
|
||||||
import { Router } from "express";
|
import { NextFunction, Request, Response, Router } from "express";
|
||||||
|
|
||||||
export const authRouter = (appRouter: Router) => {
|
export const authRouter = (appRouter: Router) => {
|
||||||
const authRoutes: Router = Router({ mergeParams: true });
|
const authRoutes: Router = Router({ mergeParams: true });
|
||||||
@ -45,7 +46,7 @@ export const authRouter = (appRouter: Router) => {
|
|||||||
"/login",
|
"/login",
|
||||||
validateRequest(LoginUserSchema),
|
validateRequest(LoginUserSchema),
|
||||||
validateTabContextHeader,
|
validateTabContextHeader,
|
||||||
(req, res, next) => {
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
createLoginController().execute(req, res, next);
|
createLoginController().execute(req, res, next);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -61,10 +62,14 @@ export const authRouter = (appRouter: Router) => {
|
|||||||
*
|
*
|
||||||
* @apiSuccess (200) {String} message Success message.
|
* @apiSuccess (200) {String} message Success message.
|
||||||
*/
|
*/
|
||||||
authRoutes.post("/logout", validateUser, validateTabContextHeader, (req, res, next) => {
|
authRoutes.post(
|
||||||
res.sendStatus(200);
|
"/logout",
|
||||||
//createLogoutController().execute(req, res, next);
|
validateUser,
|
||||||
});
|
validateTabContextHeader,
|
||||||
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
|
createLogoutController().execute(req, res, next);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
appRouter.use("/auth", authRoutes);
|
appRouter.use("/auth", authRoutes);
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user