@jmlq/auth 0.0.1-alpha.10 → 0.0.1-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/dtos/index.d.ts +1 -1
- package/dist/application/dtos/index.js +2 -1
- package/dist/application/dtos/request/change-password.request.d.ts +15 -0
- package/dist/application/dtos/request/index.d.ts +3 -0
- package/dist/application/dtos/request/index.js +4 -0
- package/dist/application/dtos/request/register-user.request.d.ts +1 -1
- package/dist/application/dtos/request/request-password-reset.request.d.ts +6 -0
- package/dist/application/dtos/request/request-password-reset.request.js +2 -0
- package/dist/application/dtos/request/reset-password.request.d.ts +14 -0
- package/dist/application/dtos/request/reset-password.request.js +2 -0
- package/dist/application/dtos/response/change-password.response.d.ts +7 -0
- package/dist/application/dtos/response/change-password.response.js +2 -0
- package/dist/application/dtos/response/index.d.ts +3 -0
- package/dist/application/dtos/response/index.js +4 -0
- package/dist/application/dtos/response/request-password-reset.response.d.ts +15 -0
- package/dist/application/dtos/response/request-password-reset.response.js +2 -0
- package/dist/application/dtos/response/reset-password.response.d.ts +7 -0
- package/dist/application/dtos/response/reset-password.response.js +2 -0
- package/dist/application/dtos/{type → types}/index.js +1 -0
- package/dist/application/dtos/types/user-role.type.js +2 -0
- package/dist/application/factories/auth-service.factory.d.ts +4 -4
- package/dist/application/factories/auth-service.factory.js +10 -2
- package/dist/application/factories/index.js +1 -0
- package/dist/application/index.js +1 -0
- package/dist/application/types/auth-service-factory-options.type.d.ts +36 -0
- package/dist/application/types/index.js +1 -0
- package/dist/application/use-cases/change-password.use-case.d.ts +21 -0
- package/dist/application/use-cases/change-password.use-case.js +49 -0
- package/dist/application/use-cases/index.d.ts +3 -0
- package/dist/application/use-cases/index.js +4 -0
- package/dist/application/use-cases/internal/index.d.ts +1 -0
- package/dist/application/use-cases/internal/index.js +18 -0
- package/dist/application/use-cases/internal/password-assertions.d.ts +13 -0
- package/dist/application/use-cases/internal/password-assertions.js +26 -0
- package/dist/application/use-cases/refresh-token.use-case.js +1 -1
- package/dist/application/use-cases/register-user.use-case.js +3 -5
- package/dist/application/use-cases/request-password-reset.use-case.d.ts +19 -0
- package/dist/application/use-cases/request-password-reset.use-case.js +43 -0
- package/dist/application/use-cases/reset-password.use-case.d.ts +20 -0
- package/dist/application/use-cases/reset-password.use-case.js +59 -0
- package/dist/domain/entities/credential.entity.d.ts +0 -5
- package/dist/domain/entities/credential.entity.js +1 -7
- package/dist/domain/entities/index.js +1 -0
- package/dist/domain/entities/user.entity.d.ts +7 -1
- package/dist/domain/entities/user.entity.js +10 -0
- package/dist/domain/errors/auth.errors.d.ts +0 -7
- package/dist/domain/errors/auth.errors.js +1 -8
- package/dist/domain/errors/identity.errors.js +1 -1
- package/dist/domain/errors/index.d.ts +1 -0
- package/dist/domain/errors/index.js +2 -0
- package/dist/domain/errors/password-reset.errors.d.ts +13 -0
- package/dist/domain/errors/password-reset.errors.js +29 -0
- package/dist/domain/index.js +1 -0
- package/dist/domain/object-values/email.js +1 -0
- package/dist/domain/object-values/hashed-password.js +1 -0
- package/dist/domain/object-values/id.js +1 -0
- package/dist/domain/object-values/index.js +1 -0
- package/dist/domain/object-values/permission.js +1 -0
- package/dist/domain/object-values/role.js +1 -0
- package/dist/domain/ports/auth/index.d.ts +1 -0
- package/dist/domain/ports/auth/index.js +2 -0
- package/dist/domain/ports/auth/password-reset-token.port.d.ts +36 -0
- package/dist/domain/ports/auth/password-reset-token.port.js +3 -0
- package/dist/domain/ports/jwt/index.js +1 -0
- package/dist/domain/ports/jwt/payload/index.js +1 -0
- package/dist/domain/ports/repository/index.js +1 -0
- package/dist/domain/ports/token/index.js +1 -0
- package/dist/domain/props/entities/index.js +1 -0
- package/dist/domain/props/index.js +1 -0
- package/dist/domain/props/jwt/index.js +1 -0
- package/dist/domain/services/index.js +1 -0
- package/dist/infrastructure/index.d.ts +3 -0
- package/dist/infrastructure/index.js +19 -0
- package/dist/infrastructure/security/index.js +1 -0
- package/dist/infrastructure/services/index.js +1 -0
- package/dist/infrastructure/services/token-session.service.js +2 -6
- package/dist/infrastructure/types/auth-service-container.d.ts +11 -2
- package/dist/infrastructure/types/index.js +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/shared/utils/index.js +1 -0
- package/dist/shared/utils/time-parser.js +1 -0
- package/package.json +2 -1
- /package/dist/application/dtos/{type/user-role.type.js → request/change-password.request.js} +0 -0
- /package/dist/application/dtos/{type → types}/index.d.ts +0 -0
- /package/dist/application/dtos/{type → types}/user-role.type.d.ts +0 -0
|
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
//src/application/dtos/index.ts
|
|
17
18
|
__exportStar(require("./request"), exports);
|
|
18
19
|
__exportStar(require("./response"), exports);
|
|
19
|
-
__exportStar(require("./
|
|
20
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cambiar password desde formulario
|
|
3
|
+
*/
|
|
4
|
+
export interface ChangePasswordRequest {
|
|
5
|
+
userId: string;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
currentPassword: string;
|
|
8
|
+
newPassword: string;
|
|
9
|
+
confirmNewPassword: string;
|
|
10
|
+
/**
|
|
11
|
+
* Si es true, revoca todas las sesiones del usuario.
|
|
12
|
+
* Si es false, revoca solo la sesión actual (sessionId).
|
|
13
|
+
*/
|
|
14
|
+
logoutAllDevices: boolean;
|
|
15
|
+
}
|
|
@@ -2,3 +2,6 @@ export * from "./login.request";
|
|
|
2
2
|
export * from "./logout.request";
|
|
3
3
|
export * from "./refresh-token.request";
|
|
4
4
|
export * from "./register-user.request";
|
|
5
|
+
export * from "./request-password-reset.request";
|
|
6
|
+
export * from "./reset-password.request";
|
|
7
|
+
export * from "./change-password.request";
|
|
@@ -14,7 +14,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
//src/application/dtos/request/index.ts
|
|
17
18
|
__exportStar(require("./login.request"), exports);
|
|
18
19
|
__exportStar(require("./logout.request"), exports);
|
|
19
20
|
__exportStar(require("./refresh-token.request"), exports);
|
|
20
21
|
__exportStar(require("./register-user.request"), exports);
|
|
22
|
+
__exportStar(require("./request-password-reset.request"), exports);
|
|
23
|
+
__exportStar(require("./reset-password.request"), exports);
|
|
24
|
+
__exportStar(require("./change-password.request"), exports);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Actualizar password usando un token una vez que se envía link a email
|
|
3
|
+
*/
|
|
4
|
+
export interface ResetPasswordRequest {
|
|
5
|
+
resetToken: string;
|
|
6
|
+
newPassword: string;
|
|
7
|
+
confirmNewPassword: string;
|
|
8
|
+
/**
|
|
9
|
+
* Política de sesiones post-reset:
|
|
10
|
+
* - true: logout global (recomendado)
|
|
11
|
+
* - false: mantener sesiones (si lo permites)
|
|
12
|
+
*/
|
|
13
|
+
logoutAllDevices?: boolean;
|
|
14
|
+
}
|
|
@@ -2,3 +2,6 @@ export * from "./login.response";
|
|
|
2
2
|
export * from "./logout.response";
|
|
3
3
|
export * from "./refresh-token.response";
|
|
4
4
|
export * from "./register-user.response";
|
|
5
|
+
export * from "./reset-password.response";
|
|
6
|
+
export * from "./request-password-reset.response";
|
|
7
|
+
export * from "./change-password.response";
|
|
@@ -14,7 +14,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
//src/application/dtos/response/index.ts
|
|
17
18
|
__exportStar(require("./login.response"), exports);
|
|
18
19
|
__exportStar(require("./logout.response"), exports);
|
|
19
20
|
__exportStar(require("./refresh-token.response"), exports);
|
|
20
21
|
__exportStar(require("./register-user.response"), exports);
|
|
22
|
+
__exportStar(require("./reset-password.response"), exports);
|
|
23
|
+
__exportStar(require("./request-password-reset.response"), exports);
|
|
24
|
+
__exportStar(require("./change-password.response"), exports);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Respuesta del caso de uso.
|
|
3
|
+
* - `message` debe ser genérico para evitar enumeración de usuarios.
|
|
4
|
+
* - `delivery` es opcional y SOLO debe usarse internamente por el API para enviar email.
|
|
5
|
+
* No se recomienda devolver `delivery` al cliente final.
|
|
6
|
+
*/
|
|
7
|
+
export interface RequestPasswordResetResponse {
|
|
8
|
+
success: true;
|
|
9
|
+
message: string;
|
|
10
|
+
delivery?: {
|
|
11
|
+
email: string;
|
|
12
|
+
resetToken: string;
|
|
13
|
+
expiresAt: string;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -14,4 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
//src/application/dtos/types/index.ts
|
|
17
18
|
__exportStar(require("./user-role.type"), exports);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { ICredentialRepositoryPort, ITokenServicePort, IUserRepositoryPort } from "../../domain";
|
|
2
|
-
import { IAuthServiceContainer } from "../../infrastructure/types";
|
|
3
|
-
import { AuthServiceFactoryOptions } from "../types";
|
|
1
|
+
import { ICredentialRepositoryPort, ITokenServicePort, IUserRepositoryPort, IPasswordResetTokenPort } from "../../domain";
|
|
2
|
+
import type { IAuthServiceContainer } from "../../infrastructure/types";
|
|
3
|
+
import type { AuthServiceFactoryOptions } from "../types";
|
|
4
4
|
/**
|
|
5
5
|
* Factory principal:
|
|
6
6
|
* - construye servicios e inyecta dependencias
|
|
7
7
|
* - encapsula configuración para que NO se repita en cada API externa
|
|
8
8
|
*/
|
|
9
9
|
export declare class AuthServiceFactory {
|
|
10
|
-
static create(userRepository: IUserRepositoryPort, credentialRepository: ICredentialRepositoryPort, tokenService: ITokenServicePort, options?: AuthServiceFactoryOptions): IAuthServiceContainer;
|
|
10
|
+
static create(userRepository: IUserRepositoryPort, credentialRepository: ICredentialRepositoryPort, tokenService: ITokenServicePort, passwordResetToken: IPasswordResetTokenPort, options?: AuthServiceFactoryOptions): IAuthServiceContainer;
|
|
11
11
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AuthServiceFactory = void 0;
|
|
4
|
-
//
|
|
4
|
+
//src/application/factories/auth-service.factory.ts
|
|
5
5
|
const services_1 = require("../../domain/services");
|
|
6
6
|
const use_cases_1 = require("../use-cases");
|
|
7
7
|
const security_1 = require("../../infrastructure/security");
|
|
@@ -12,7 +12,7 @@ const services_2 = require("../../infrastructure/services");
|
|
|
12
12
|
* - encapsula configuración para que NO se repita en cada API externa
|
|
13
13
|
*/
|
|
14
14
|
class AuthServiceFactory {
|
|
15
|
-
static create(userRepository, credentialRepository, tokenService, options) {
|
|
15
|
+
static create(userRepository, credentialRepository, tokenService, passwordResetToken, options) {
|
|
16
16
|
// 1) Policy + hasher
|
|
17
17
|
const passwordPolicy = new services_1.DefaultPasswordPolicy();
|
|
18
18
|
const passwordHasher = new security_1.BcryptPasswordHasher(options?.bcryptSaltRounds);
|
|
@@ -23,6 +23,10 @@ class AuthServiceFactory {
|
|
|
23
23
|
const loginWithPasswordUseCase = new use_cases_1.LoginWithPasswordUseCase(userRepository, passwordHasher, tokenSession);
|
|
24
24
|
const refreshTokenUseCase = new use_cases_1.RefreshTokenUseCase(tokenSession);
|
|
25
25
|
const logoutUseCase = new use_cases_1.LogoutUseCase(tokenSession);
|
|
26
|
+
// 4) Use cases nuevos (password flows)
|
|
27
|
+
const requestPasswordResetUseCase = new use_cases_1.RequestPasswordResetUseCase(userRepository, passwordResetToken, { resetTokenTtl: options?.passwordResetTokenTtl });
|
|
28
|
+
const resetPasswordUseCase = new use_cases_1.ResetPasswordUseCase(userRepository, credentialRepository, passwordHasher, passwordPolicy, passwordResetToken);
|
|
29
|
+
const changePasswordUseCase = new use_cases_1.ChangePasswordUseCase(userRepository, credentialRepository, passwordHasher, passwordPolicy);
|
|
26
30
|
return {
|
|
27
31
|
userRepository,
|
|
28
32
|
credentialRepository,
|
|
@@ -30,10 +34,14 @@ class AuthServiceFactory {
|
|
|
30
34
|
tokenService,
|
|
31
35
|
passwordPolicy,
|
|
32
36
|
tokenSession,
|
|
37
|
+
passwordResetToken,
|
|
33
38
|
registerUserUseCase,
|
|
34
39
|
loginWithPasswordUseCase,
|
|
35
40
|
refreshTokenUseCase,
|
|
36
41
|
logoutUseCase,
|
|
42
|
+
requestPasswordResetUseCase,
|
|
43
|
+
resetPasswordUseCase,
|
|
44
|
+
changePasswordUseCase,
|
|
37
45
|
};
|
|
38
46
|
}
|
|
39
47
|
}
|
|
@@ -14,4 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
//src/application/factories/index.ts
|
|
17
18
|
__exportStar(require("./auth-service.factory"), exports);
|
|
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
//src/application/index.ts
|
|
17
18
|
__exportStar(require("./dtos"), exports);
|
|
18
19
|
__exportStar(require("./factories"), exports);
|
|
19
20
|
__exportStar(require("./use-cases"), exports);
|
|
@@ -1,5 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permite ajustar parámetros operativos de Auth sin conocer ni tocar la construcción interna del core.
|
|
3
|
+
* Es decir: configura “políticas” y “valores por defecto” que la factory usará al armar el container.
|
|
4
|
+
*/
|
|
1
5
|
export interface AuthServiceFactoryOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Controla el costo computacional del hashing con bcrypt
|
|
8
|
+
* - Qué hace: define cuántas rondas (work factor) usa bcrypt al generar hashes.
|
|
9
|
+
* - Impacto:
|
|
10
|
+
* - Mayor valor ⇒ más seguro contra ataques de fuerza bruta, pero más CPU/latencia en register/login/change/reset password.
|
|
11
|
+
* - Menor valor ⇒ más rápido, pero menos robusto.
|
|
12
|
+
*/
|
|
2
13
|
bcryptSaltRounds?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Define el tiempo de vida por defecto de los access tokens.
|
|
16
|
+
* - Qué hace: determina el exp (expiración) con el que se generan access tokens (si tu tokenSession/token service usa este default).
|
|
17
|
+
* - Formato: string “humana” (ej. "15m", "1h"), que el core normaliza.
|
|
18
|
+
* - Impacto:
|
|
19
|
+
* - Más corto ⇒ más seguridad, más refresh frecuente.
|
|
20
|
+
* - Más largo ⇒ mejor UX, más riesgo si el token se filtra.
|
|
21
|
+
*/
|
|
3
22
|
accessTokenTtl?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Define el tiempo de vida por defecto de los refresh tokens.
|
|
25
|
+
* - Qué hace: determina la expiración del refresh token (el que permite rotar/renovar access tokens).
|
|
26
|
+
* - Formato: "7d", "30d", etc.
|
|
27
|
+
* - Impacto:
|
|
28
|
+
* - Más corto ⇒ limita ventana de secuestro de sesión, pero obliga re-login más seguido.
|
|
29
|
+
* - Más largo ⇒ sesiones persistentes, más riesgo si el refresh token se compromete.
|
|
30
|
+
*/
|
|
4
31
|
refreshTokenTtl?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Define el tiempo de vida del token de recuperación de contraseña (RememberPassword).
|
|
34
|
+
* - Qué hace: controla cuánto dura el token que se entrega en el email de “reset password”.
|
|
35
|
+
* - Formato: "15m", "30m", "1h", etc
|
|
36
|
+
* - Impacto:
|
|
37
|
+
* - Más corto ⇒ más seguro (menos ventana), pero el usuario puede no alcanzar a usarlo.
|
|
38
|
+
* - Más largo ⇒ mejor conveniencia, pero incrementa exposición.
|
|
39
|
+
*/
|
|
40
|
+
passwordResetTokenTtl?: string;
|
|
5
41
|
}
|
|
@@ -14,4 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
//src/application/types/index.ts
|
|
17
18
|
__exportStar(require("./auth-service-factory-options.type"), exports);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ICredentialRepositoryPort, IPasswordHasherPort, IPasswordPolicyPort, IUserRepositoryPort } from "../../domain/ports";
|
|
2
|
+
import { ChangePasswordRequest, ChangePasswordResponse } from "../dtos";
|
|
3
|
+
/**
|
|
4
|
+
* Cambia contraseña con validación de password actual.
|
|
5
|
+
*
|
|
6
|
+
* - Valida confirmación
|
|
7
|
+
* - Valida policy
|
|
8
|
+
* - Verifica password actual
|
|
9
|
+
* - Cambia hash
|
|
10
|
+
* - Revoca sesiones:
|
|
11
|
+
* - logoutAllDevices=true => revoca todas
|
|
12
|
+
* - false => revoca solo la sesión actual (sessionId)
|
|
13
|
+
*/
|
|
14
|
+
export declare class ChangePasswordUseCase {
|
|
15
|
+
private readonly userRepository;
|
|
16
|
+
private readonly credentialRepository;
|
|
17
|
+
private readonly passwordHasher;
|
|
18
|
+
private readonly passwordPolicy;
|
|
19
|
+
constructor(userRepository: IUserRepositoryPort, credentialRepository: ICredentialRepositoryPort, passwordHasher: IPasswordHasherPort, passwordPolicy: IPasswordPolicyPort);
|
|
20
|
+
execute(request: ChangePasswordRequest): Promise<ChangePasswordResponse>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ChangePasswordUseCase = void 0;
|
|
4
|
+
const object_values_1 = require("../../domain/object-values");
|
|
5
|
+
const errors_1 = require("../../domain/errors");
|
|
6
|
+
const internal_1 = require("./internal");
|
|
7
|
+
/**
|
|
8
|
+
* Cambia contraseña con validación de password actual.
|
|
9
|
+
*
|
|
10
|
+
* - Valida confirmación
|
|
11
|
+
* - Valida policy
|
|
12
|
+
* - Verifica password actual
|
|
13
|
+
* - Cambia hash
|
|
14
|
+
* - Revoca sesiones:
|
|
15
|
+
* - logoutAllDevices=true => revoca todas
|
|
16
|
+
* - false => revoca solo la sesión actual (sessionId)
|
|
17
|
+
*/
|
|
18
|
+
class ChangePasswordUseCase {
|
|
19
|
+
constructor(userRepository, credentialRepository, passwordHasher, passwordPolicy) {
|
|
20
|
+
this.userRepository = userRepository;
|
|
21
|
+
this.credentialRepository = credentialRepository;
|
|
22
|
+
this.passwordHasher = passwordHasher;
|
|
23
|
+
this.passwordPolicy = passwordPolicy;
|
|
24
|
+
}
|
|
25
|
+
async execute(request) {
|
|
26
|
+
(0, internal_1.assertPasswordsMatch)(request.newPassword, request.confirmNewPassword);
|
|
27
|
+
(0, internal_1.assertPasswordPolicy)(this.passwordPolicy, request.newPassword);
|
|
28
|
+
const user = await this.userRepository.findById(new object_values_1.Id(request.userId));
|
|
29
|
+
if (!user)
|
|
30
|
+
throw new errors_1.UserNotFoundError("User not found");
|
|
31
|
+
const currentHash = user.password.serialize();
|
|
32
|
+
const ok = await this.passwordHasher.compare(request.currentPassword, currentHash);
|
|
33
|
+
if (!ok) {
|
|
34
|
+
throw new errors_1.PasswordMismatchError("Current password is invalid");
|
|
35
|
+
}
|
|
36
|
+
const newHash = await this.passwordHasher.hash(request.newPassword);
|
|
37
|
+
user.changePassword(new object_values_1.HashedPassword(newHash));
|
|
38
|
+
await this.userRepository.update(user);
|
|
39
|
+
const userId = user.id;
|
|
40
|
+
if (request.logoutAllDevices) {
|
|
41
|
+
await this.credentialRepository.deleteByUserId(userId);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
await this.credentialRepository.deleteBySessionId(new object_values_1.Id(request.sessionId));
|
|
45
|
+
}
|
|
46
|
+
return { success: true, message: "Password changed successfully" };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.ChangePasswordUseCase = ChangePasswordUseCase;
|
|
@@ -2,3 +2,6 @@ export * from "./login-with-password.use-case";
|
|
|
2
2
|
export * from "./logout.use-case";
|
|
3
3
|
export * from "./refresh-token.use-case";
|
|
4
4
|
export * from "./register-user.use-case";
|
|
5
|
+
export * from "./change-password.use-case";
|
|
6
|
+
export * from "./request-password-reset.use-case";
|
|
7
|
+
export * from "./reset-password.use-case";
|
|
@@ -14,7 +14,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
//src/application/use-cases/index.ts
|
|
17
18
|
__exportStar(require("./login-with-password.use-case"), exports);
|
|
18
19
|
__exportStar(require("./logout.use-case"), exports);
|
|
19
20
|
__exportStar(require("./refresh-token.use-case"), exports);
|
|
20
21
|
__exportStar(require("./register-user.use-case"), exports);
|
|
22
|
+
__exportStar(require("./change-password.use-case"), exports);
|
|
23
|
+
__exportStar(require("./request-password-reset.use-case"), exports);
|
|
24
|
+
__exportStar(require("./reset-password.use-case"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./password-assertions";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
//src/application/use-cases/internal/index.ts
|
|
18
|
+
__exportStar(require("./password-assertions"), exports);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IPasswordPolicyPort } from "../../../domain/ports";
|
|
2
|
+
/**
|
|
3
|
+
* Asserts that `newPassword` equals `confirmPassword`.
|
|
4
|
+
*
|
|
5
|
+
* Application-level validation to keep use cases small and consistent.
|
|
6
|
+
*/
|
|
7
|
+
export declare function assertPasswordsMatch(newPassword: string, confirmPassword: string): void;
|
|
8
|
+
/**
|
|
9
|
+
* Asserts that `password` satisfies the configured password policy.
|
|
10
|
+
*
|
|
11
|
+
* This runs BEFORE hashing to fail fast and reduce CPU cost.
|
|
12
|
+
*/
|
|
13
|
+
export declare function assertPasswordPolicy(passwordPolicy: IPasswordPolicyPort, password: string): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertPasswordsMatch = assertPasswordsMatch;
|
|
4
|
+
exports.assertPasswordPolicy = assertPasswordPolicy;
|
|
5
|
+
const errors_1 = require("../../../domain/errors");
|
|
6
|
+
/**
|
|
7
|
+
* Asserts that `newPassword` equals `confirmPassword`.
|
|
8
|
+
*
|
|
9
|
+
* Application-level validation to keep use cases small and consistent.
|
|
10
|
+
*/
|
|
11
|
+
function assertPasswordsMatch(newPassword, confirmPassword) {
|
|
12
|
+
if (newPassword !== confirmPassword) {
|
|
13
|
+
throw new errors_1.PasswordMismatchError("Passwords do not match");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Asserts that `password` satisfies the configured password policy.
|
|
18
|
+
*
|
|
19
|
+
* This runs BEFORE hashing to fail fast and reduce CPU cost.
|
|
20
|
+
*/
|
|
21
|
+
function assertPasswordPolicy(passwordPolicy, password) {
|
|
22
|
+
const strength = passwordPolicy.validateStrength(password);
|
|
23
|
+
if (!strength.isValid) {
|
|
24
|
+
throw new errors_1.PasswordPolicyViolationError(strength.errors);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RegisterUserUseCase = void 0;
|
|
4
4
|
//src/application/use-cases/register-user.use-case.ts
|
|
5
5
|
const domain_1 = require("../../domain");
|
|
6
|
+
const internal_1 = require("./internal");
|
|
6
7
|
class RegisterUserUseCase {
|
|
7
8
|
constructor(userRepository, passwordHasher, passwordPolicy) {
|
|
8
9
|
this.userRepository = userRepository;
|
|
@@ -33,11 +34,8 @@ class RegisterUserUseCase {
|
|
|
33
34
|
// Use case
|
|
34
35
|
// ---------------------------------------------------------------------------
|
|
35
36
|
async execute(request) {
|
|
36
|
-
// Validar
|
|
37
|
-
|
|
38
|
-
if (!passwordValidation.isValid) {
|
|
39
|
-
throw new domain_1.PasswordPolicyViolationError(passwordValidation.errors);
|
|
40
|
-
}
|
|
37
|
+
// Validar policy antes de hacer trabajo costoso (hash)
|
|
38
|
+
(0, internal_1.assertPasswordPolicy)(this.passwordPolicy, request.password);
|
|
41
39
|
// Verificar que el email no esté en uso
|
|
42
40
|
const email = new domain_1.Email(request.email);
|
|
43
41
|
const exists = await this.userRepository.findByEmail(email);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { IUserRepositoryPort } from "../../domain/ports";
|
|
2
|
+
import type { IPasswordResetTokenPort } from "../../domain/ports/auth/password-reset-token.port";
|
|
3
|
+
import { RequestPasswordResetRequest, RequestPasswordResetResponse } from "../dtos";
|
|
4
|
+
/**
|
|
5
|
+
* Solicita reset de contraseña.
|
|
6
|
+
*
|
|
7
|
+
* - Anti-enumeración: siempre retorna el mismo mensaje de éxito.
|
|
8
|
+
* - Si el usuario existe, retorna `delivery` para que el API envíe el email.
|
|
9
|
+
* - NO envía emails (el API lo hace con @jmlq/mailer).
|
|
10
|
+
*/
|
|
11
|
+
export declare class RequestPasswordResetUseCase {
|
|
12
|
+
private readonly userRepository;
|
|
13
|
+
private readonly passwordResetToken;
|
|
14
|
+
private readonly resetTokenTtl;
|
|
15
|
+
constructor(userRepository: IUserRepositoryPort, passwordResetToken: IPasswordResetTokenPort, opts?: {
|
|
16
|
+
resetTokenTtl?: string;
|
|
17
|
+
});
|
|
18
|
+
execute(request: RequestPasswordResetRequest): Promise<RequestPasswordResetResponse>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RequestPasswordResetUseCase = void 0;
|
|
4
|
+
const object_values_1 = require("../../domain/object-values");
|
|
5
|
+
const time_parser_1 = require("../../shared/utils/time-parser");
|
|
6
|
+
/**
|
|
7
|
+
* Solicita reset de contraseña.
|
|
8
|
+
*
|
|
9
|
+
* - Anti-enumeración: siempre retorna el mismo mensaje de éxito.
|
|
10
|
+
* - Si el usuario existe, retorna `delivery` para que el API envíe el email.
|
|
11
|
+
* - NO envía emails (el API lo hace con @jmlq/mailer).
|
|
12
|
+
*/
|
|
13
|
+
class RequestPasswordResetUseCase {
|
|
14
|
+
constructor(userRepository, passwordResetToken, opts) {
|
|
15
|
+
this.userRepository = userRepository;
|
|
16
|
+
this.passwordResetToken = passwordResetToken;
|
|
17
|
+
// Default conservador: 15 minutos
|
|
18
|
+
this.resetTokenTtl = opts?.resetTokenTtl ?? "15m";
|
|
19
|
+
}
|
|
20
|
+
async execute(request) {
|
|
21
|
+
const message = "A link to reset your password has been sent to your email address.";
|
|
22
|
+
// Normaliza y valida el email vía VO
|
|
23
|
+
const email = new object_values_1.Email(request.email);
|
|
24
|
+
// Buscar usuario (no revelar resultado al cliente final)
|
|
25
|
+
const user = await this.userRepository.findByEmail(email);
|
|
26
|
+
if (!user) {
|
|
27
|
+
return { success: true, message };
|
|
28
|
+
}
|
|
29
|
+
// Emite token con TTL (reutiliza utility existente)
|
|
30
|
+
const ttlMs = time_parser_1.TimeParser.parseToMilliseconds(this.resetTokenTtl);
|
|
31
|
+
const issued = await this.passwordResetToken.issue(user.id, ttlMs);
|
|
32
|
+
return {
|
|
33
|
+
success: true,
|
|
34
|
+
message,
|
|
35
|
+
delivery: {
|
|
36
|
+
email: user.email.getValue(),
|
|
37
|
+
resetToken: issued.token,
|
|
38
|
+
expiresAt: issued.expiresAt.toISOString(),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.RequestPasswordResetUseCase = RequestPasswordResetUseCase;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ICredentialRepositoryPort, IPasswordHasherPort, IPasswordPolicyPort, IUserRepositoryPort } from "../../domain/ports";
|
|
2
|
+
import type { IPasswordResetTokenPort } from "../../domain/ports/auth/password-reset-token.port";
|
|
3
|
+
import { ResetPasswordRequest, ResetPasswordResponse } from "../dtos";
|
|
4
|
+
/**
|
|
5
|
+
* Confirma el reseteo de contraseña usando token (single-use).
|
|
6
|
+
*
|
|
7
|
+
* - Verifica y consume token
|
|
8
|
+
* - Aplica policy
|
|
9
|
+
* - Cambia hash del usuario
|
|
10
|
+
* - Revoca sesiones según política (por defecto: logout global recomendado)
|
|
11
|
+
*/
|
|
12
|
+
export declare class ResetPasswordUseCase {
|
|
13
|
+
private readonly userRepository;
|
|
14
|
+
private readonly credentialRepository;
|
|
15
|
+
private readonly passwordHasher;
|
|
16
|
+
private readonly passwordPolicy;
|
|
17
|
+
private readonly passwordResetToken;
|
|
18
|
+
constructor(userRepository: IUserRepositoryPort, credentialRepository: ICredentialRepositoryPort, passwordHasher: IPasswordHasherPort, passwordPolicy: IPasswordPolicyPort, passwordResetToken: IPasswordResetTokenPort);
|
|
19
|
+
execute(request: ResetPasswordRequest): Promise<ResetPasswordResponse>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResetPasswordUseCase = void 0;
|
|
4
|
+
const object_values_1 = require("../../domain/object-values");
|
|
5
|
+
const errors_1 = require("../../domain/errors");
|
|
6
|
+
const password_reset_errors_1 = require("../../domain/errors/password-reset.errors");
|
|
7
|
+
const internal_1 = require("./internal");
|
|
8
|
+
/**
|
|
9
|
+
* Confirma el reseteo de contraseña usando token (single-use).
|
|
10
|
+
*
|
|
11
|
+
* - Verifica y consume token
|
|
12
|
+
* - Aplica policy
|
|
13
|
+
* - Cambia hash del usuario
|
|
14
|
+
* - Revoca sesiones según política (por defecto: logout global recomendado)
|
|
15
|
+
*/
|
|
16
|
+
class ResetPasswordUseCase {
|
|
17
|
+
constructor(userRepository, credentialRepository, passwordHasher, passwordPolicy, passwordResetToken) {
|
|
18
|
+
this.userRepository = userRepository;
|
|
19
|
+
this.credentialRepository = credentialRepository;
|
|
20
|
+
this.passwordHasher = passwordHasher;
|
|
21
|
+
this.passwordPolicy = passwordPolicy;
|
|
22
|
+
this.passwordResetToken = passwordResetToken;
|
|
23
|
+
}
|
|
24
|
+
async execute(request) {
|
|
25
|
+
// Validación de request (application)
|
|
26
|
+
(0, internal_1.assertPasswordsMatch)(request.newPassword, request.confirmNewPassword);
|
|
27
|
+
(0, internal_1.assertPasswordPolicy)(this.passwordPolicy, request.newPassword);
|
|
28
|
+
// Consumir token (single-use). La implementación debe asegurar atomicidad.
|
|
29
|
+
const consumed = await this.passwordResetToken
|
|
30
|
+
.consume(request.resetToken)
|
|
31
|
+
.catch((e) => {
|
|
32
|
+
// Mantener errores estables: no filtrar detalles del storage
|
|
33
|
+
// Nota: si quieres distinguir invalid vs used, puedes mapear en el adapter.
|
|
34
|
+
throw new password_reset_errors_1.PasswordResetTokenInvalidError(e instanceof Error ? e.message : "Password reset token is invalid");
|
|
35
|
+
});
|
|
36
|
+
// Verificar expiración por seguridad adicional (aunque el adapter debería hacerlo)
|
|
37
|
+
if (new Date() > consumed.expiresAt) {
|
|
38
|
+
throw new password_reset_errors_1.PasswordResetTokenExpiredError();
|
|
39
|
+
}
|
|
40
|
+
const userId = consumed.userId;
|
|
41
|
+
const user = await this.userRepository.findById(userId);
|
|
42
|
+
if (!user) {
|
|
43
|
+
// No debería pasar si token fue emitido correctamente, pero es seguro.
|
|
44
|
+
throw new errors_1.UserNotFoundError("User not found");
|
|
45
|
+
}
|
|
46
|
+
// Hash + cambio de password en el agregado
|
|
47
|
+
const newHash = await this.passwordHasher.hash(request.newPassword);
|
|
48
|
+
user.changePassword(new object_values_1.HashedPassword(newHash));
|
|
49
|
+
await this.userRepository.update(user);
|
|
50
|
+
// Política de sesiones:
|
|
51
|
+
// - default: logout global (recomendación de seguridad)
|
|
52
|
+
const logoutAll = request.logoutAllDevices ?? true;
|
|
53
|
+
if (logoutAll) {
|
|
54
|
+
await this.credentialRepository.deleteByUserId(userId);
|
|
55
|
+
}
|
|
56
|
+
return { success: true, message: "Password has been reset successfully" };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.ResetPasswordUseCase = ResetPasswordUseCase;
|