cca-auth-module 0.2.0 → 0.2.2
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/README.md +385 -395
- package/dist/application/useCase/LogoutUseCase.d.ts +1 -1
- package/dist/domain/interfaces/IAuthService.d.ts +2 -2
- package/dist/domain/interfaces/IDecodedToken.d.ts +1 -0
- package/dist/domain/interfaces/IJwtAuth.d.ts +4 -4
- package/dist/domain/interfaces/IJwtPayload.d.ts +1 -0
- package/dist/index.d.mts +7 -6
- package/dist/index.d.ts +7 -6
- package/dist/index.js +136 -58
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +136 -58
- package/dist/index.mjs.map +1 -1
- package/dist/infrastructure/repository/AuthRepository.d.ts +2 -1
- package/dist/infrastructure/services/JwtAuthService.d.ts +1 -2
- package/package.json +19 -16
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AdminEntity, UserEntity, UserRole } from "cca-entities";
|
|
2
2
|
import { IDecodedToken } from "./IDecodedToken";
|
|
3
3
|
export interface IAuthService {
|
|
4
|
-
generateAccessToken(user: UserEntity | AdminEntity, role: UserRole): string;
|
|
4
|
+
generateAccessToken(user: UserEntity | AdminEntity, role: UserRole, twoFactorAuthenticated?: boolean): string;
|
|
5
5
|
generateRefreshToken(user: UserEntity | AdminEntity): string;
|
|
6
6
|
verifyAccessToken(token: string): Promise<IDecodedToken>;
|
|
7
|
-
verifyRefreshToken(token: string): IDecodedToken
|
|
7
|
+
verifyRefreshToken(token: string): Promise<IDecodedToken>;
|
|
8
8
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { AuthEntity, UserEntity, UserRole } from "cca-entities";
|
|
1
|
+
import { AdminEntity, AuthEntity, UserEntity, UserRole } from "cca-entities";
|
|
2
2
|
import { IDecodedToken } from "./IDecodedToken";
|
|
3
3
|
export interface IJwtAuth {
|
|
4
4
|
validateUser(email: string, password: string): Promise<AuthEntity | null>;
|
|
5
|
-
generateAccessToken(user: UserEntity, role: UserRole): string;
|
|
6
|
-
generateRefreshToken(user: UserEntity): string;
|
|
5
|
+
generateAccessToken(user: UserEntity | AdminEntity, role: UserRole, twoFactorAuthenticated?: boolean): string;
|
|
6
|
+
generateRefreshToken(user: UserEntity | AdminEntity): string;
|
|
7
7
|
verifyAccessToken(token: string): Promise<IDecodedToken>;
|
|
8
|
-
verifyRefreshToken(token: string): IDecodedToken
|
|
8
|
+
verifyRefreshToken(token: string): Promise<IDecodedToken>;
|
|
9
9
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseRepository, IExtendedBaseRepository, IBaseService, BaseDatabase, BaseContainer } from 'cca-core';
|
|
2
2
|
import { Request, Response, NextFunction } from 'express';
|
|
3
|
-
import { AuthEntity,
|
|
3
|
+
import { AuthEntity, UserEntity, AdminEntity, UserRole } from 'cca-entities';
|
|
4
4
|
import { Repository } from 'typeorm';
|
|
5
5
|
import * as jwt from 'jsonwebtoken';
|
|
6
6
|
import { JwtPayload } from 'jsonwebtoken';
|
|
@@ -32,6 +32,7 @@ declare class AuthRepository extends BaseRepository<AuthEntity> implements IExte
|
|
|
32
32
|
disableTwoFactor(auth: AuthEntity): Promise<void>;
|
|
33
33
|
isTwoFactorEnabled(userId: string): Promise<boolean>;
|
|
34
34
|
getTwoFactorSecret(userId: string): Promise<string | null>;
|
|
35
|
+
saveAccount(account: UserEntity | AdminEntity): Promise<void>;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
declare class RegisterUseCase implements IBaseService {
|
|
@@ -64,24 +65,24 @@ interface IDecodedToken extends JwtPayload {
|
|
|
64
65
|
userId?: string;
|
|
65
66
|
email?: string;
|
|
66
67
|
role?: string;
|
|
68
|
+
twoFactorAuthenticated?: boolean;
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
interface IAuthService {
|
|
70
|
-
generateAccessToken(user: UserEntity | AdminEntity, role: UserRole): string;
|
|
72
|
+
generateAccessToken(user: UserEntity | AdminEntity, role: UserRole, twoFactorAuthenticated?: boolean): string;
|
|
71
73
|
generateRefreshToken(user: UserEntity | AdminEntity): string;
|
|
72
74
|
verifyAccessToken(token: string): Promise<IDecodedToken>;
|
|
73
|
-
verifyRefreshToken(token: string): IDecodedToken
|
|
75
|
+
verifyRefreshToken(token: string): Promise<IDecodedToken>;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
declare class JwtAuthService implements IBaseService, IAuthService {
|
|
77
79
|
private readonly repository;
|
|
78
80
|
private jwtConfig;
|
|
79
81
|
constructor(repository: AuthRepository, config?: IJwtConfig);
|
|
80
|
-
private loadConfig;
|
|
81
82
|
initialize(): Promise<void>;
|
|
82
83
|
private validateConfiguration;
|
|
83
84
|
private verifyJwtConfig;
|
|
84
|
-
generateAccessToken(user: UserEntity | AdminEntity, role: UserRole): string;
|
|
85
|
+
generateAccessToken(user: UserEntity | AdminEntity, role: UserRole, twoFactorAuthenticated?: boolean): string;
|
|
85
86
|
generateRefreshToken(user: UserEntity | AdminEntity): string;
|
|
86
87
|
verifyToken(token: string, secret: string): Promise<IDecodedToken>;
|
|
87
88
|
verifyAccessToken(token: string): Promise<IDecodedToken>;
|
|
@@ -105,7 +106,7 @@ declare class LogoutUseCase implements IBaseService {
|
|
|
105
106
|
private readonly repository;
|
|
106
107
|
constructor(repository: AuthRepository);
|
|
107
108
|
initialize(): Promise<void>;
|
|
108
|
-
execute(
|
|
109
|
+
execute(userId: string | undefined): Promise<void>;
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
interface ITokenPair {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseRepository, IExtendedBaseRepository, IBaseService, BaseDatabase, BaseContainer } from 'cca-core';
|
|
2
2
|
import { Request, Response, NextFunction } from 'express';
|
|
3
|
-
import { AuthEntity,
|
|
3
|
+
import { AuthEntity, UserEntity, AdminEntity, UserRole } from 'cca-entities';
|
|
4
4
|
import { Repository } from 'typeorm';
|
|
5
5
|
import * as jwt from 'jsonwebtoken';
|
|
6
6
|
import { JwtPayload } from 'jsonwebtoken';
|
|
@@ -32,6 +32,7 @@ declare class AuthRepository extends BaseRepository<AuthEntity> implements IExte
|
|
|
32
32
|
disableTwoFactor(auth: AuthEntity): Promise<void>;
|
|
33
33
|
isTwoFactorEnabled(userId: string): Promise<boolean>;
|
|
34
34
|
getTwoFactorSecret(userId: string): Promise<string | null>;
|
|
35
|
+
saveAccount(account: UserEntity | AdminEntity): Promise<void>;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
declare class RegisterUseCase implements IBaseService {
|
|
@@ -64,24 +65,24 @@ interface IDecodedToken extends JwtPayload {
|
|
|
64
65
|
userId?: string;
|
|
65
66
|
email?: string;
|
|
66
67
|
role?: string;
|
|
68
|
+
twoFactorAuthenticated?: boolean;
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
interface IAuthService {
|
|
70
|
-
generateAccessToken(user: UserEntity | AdminEntity, role: UserRole): string;
|
|
72
|
+
generateAccessToken(user: UserEntity | AdminEntity, role: UserRole, twoFactorAuthenticated?: boolean): string;
|
|
71
73
|
generateRefreshToken(user: UserEntity | AdminEntity): string;
|
|
72
74
|
verifyAccessToken(token: string): Promise<IDecodedToken>;
|
|
73
|
-
verifyRefreshToken(token: string): IDecodedToken
|
|
75
|
+
verifyRefreshToken(token: string): Promise<IDecodedToken>;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
declare class JwtAuthService implements IBaseService, IAuthService {
|
|
77
79
|
private readonly repository;
|
|
78
80
|
private jwtConfig;
|
|
79
81
|
constructor(repository: AuthRepository, config?: IJwtConfig);
|
|
80
|
-
private loadConfig;
|
|
81
82
|
initialize(): Promise<void>;
|
|
82
83
|
private validateConfiguration;
|
|
83
84
|
private verifyJwtConfig;
|
|
84
|
-
generateAccessToken(user: UserEntity | AdminEntity, role: UserRole): string;
|
|
85
|
+
generateAccessToken(user: UserEntity | AdminEntity, role: UserRole, twoFactorAuthenticated?: boolean): string;
|
|
85
86
|
generateRefreshToken(user: UserEntity | AdminEntity): string;
|
|
86
87
|
verifyToken(token: string, secret: string): Promise<IDecodedToken>;
|
|
87
88
|
verifyAccessToken(token: string): Promise<IDecodedToken>;
|
|
@@ -105,7 +106,7 @@ declare class LogoutUseCase implements IBaseService {
|
|
|
105
106
|
private readonly repository;
|
|
106
107
|
constructor(repository: AuthRepository);
|
|
107
108
|
initialize(): Promise<void>;
|
|
108
|
-
execute(
|
|
109
|
+
execute(userId: string | undefined): Promise<void>;
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
interface ITokenPair {
|
package/dist/index.js
CHANGED
|
@@ -174,6 +174,7 @@ var import_jwt_decode = require("jwt-decode");
|
|
|
174
174
|
var yup = __toESM(require("yup"));
|
|
175
175
|
var import_cca_entities = require("cca-entities");
|
|
176
176
|
var import_bcrypt = __toESM(require("bcrypt"));
|
|
177
|
+
var import_crypto = __toESM(require("crypto"));
|
|
177
178
|
var schemas = {
|
|
178
179
|
id: yup.string().uuid("Invalid user ID format"),
|
|
179
180
|
email: yup.string().email("Invalid email format").max(255, "Email cannot exceed 255 characters"),
|
|
@@ -186,8 +187,9 @@ var schemas = {
|
|
|
186
187
|
};
|
|
187
188
|
var validateEmail = /* @__PURE__ */ __name(async (email, repository) => {
|
|
188
189
|
try {
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
const normalizedEmail = email?.trim().toLowerCase();
|
|
191
|
+
await schemas.email.validate(normalizedEmail);
|
|
192
|
+
const user = await repository.findByEmail(normalizedEmail);
|
|
191
193
|
if (!user) {
|
|
192
194
|
throw new NotFoundError(
|
|
193
195
|
"The email address or password is incorrect. Please retry"
|
|
@@ -209,8 +211,9 @@ var validatePassword = /* @__PURE__ */ __name(async (password) => {
|
|
|
209
211
|
}, "validatePassword");
|
|
210
212
|
var validateEmailUniqueness = /* @__PURE__ */ __name(async (repository, email, excludeUserId) => {
|
|
211
213
|
try {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
+
const normalizedEmail = email?.trim().toLowerCase();
|
|
215
|
+
await schemas.email.validate(normalizedEmail);
|
|
216
|
+
const existingUser = await repository.findByEmail(normalizedEmail);
|
|
214
217
|
if (!existingUser) return;
|
|
215
218
|
if (existingUser.id === excludeUserId) return;
|
|
216
219
|
throw new ValidationError(`Email ${email} is already in use.`);
|
|
@@ -255,7 +258,14 @@ var validateAdminSecret = /* @__PURE__ */ __name(async (secretPassword) => {
|
|
|
255
258
|
if (!config.adminSecretPassword) {
|
|
256
259
|
throw new ValidationError("ADMIN_SECRET_PASSWORD not found in config");
|
|
257
260
|
}
|
|
258
|
-
|
|
261
|
+
const provided = secretPassword.trim();
|
|
262
|
+
const expected = config.adminSecretPassword.trim();
|
|
263
|
+
if (provided.length !== expected.length) {
|
|
264
|
+
throw new ValidationError("Invalid admin password");
|
|
265
|
+
}
|
|
266
|
+
const providedBuf = Buffer.from(provided);
|
|
267
|
+
const expectedBuf = Buffer.from(expected);
|
|
268
|
+
if (!import_crypto.default.timingSafeEqual(providedBuf, expectedBuf)) {
|
|
259
269
|
throw new ValidationError("Invalid admin password");
|
|
260
270
|
}
|
|
261
271
|
} catch (error) {
|
|
@@ -285,8 +295,9 @@ var _LoginUseCase = class _LoginUseCase {
|
|
|
285
295
|
if (!account) {
|
|
286
296
|
throw new NotFoundError(`${isAdmin ? "Admin" : "User"} account not found or inactive`);
|
|
287
297
|
}
|
|
288
|
-
const accessToken = this.jwtService.generateAccessToken(account, auth.role);
|
|
289
|
-
const
|
|
298
|
+
const accessToken = this.jwtService.generateAccessToken(account, auth.role, false);
|
|
299
|
+
const decoded = (0, import_jwt_decode.jwtDecode)(accessToken);
|
|
300
|
+
const expiresAt = decoded.exp ?? 0;
|
|
290
301
|
return { id: account.id, accessToken, expiresAt, enabled: auth.twoFactorEnabled };
|
|
291
302
|
}
|
|
292
303
|
};
|
|
@@ -302,11 +313,17 @@ var _LogoutUseCase = class _LogoutUseCase {
|
|
|
302
313
|
async initialize() {
|
|
303
314
|
await (0, import_cca_core2.validateRepository)(this.repository, (repo) => repo.getAll());
|
|
304
315
|
}
|
|
305
|
-
async execute(
|
|
316
|
+
async execute(userId) {
|
|
306
317
|
try {
|
|
307
|
-
|
|
318
|
+
if (!userId) {
|
|
319
|
+
throw new NotFoundError("User ID is required");
|
|
320
|
+
}
|
|
321
|
+
await this.repository.logout(userId);
|
|
308
322
|
} catch (error) {
|
|
309
|
-
|
|
323
|
+
if (error instanceof NotFoundError) {
|
|
324
|
+
throw error;
|
|
325
|
+
}
|
|
326
|
+
throw new NotFoundError("Auth not found");
|
|
310
327
|
}
|
|
311
328
|
}
|
|
312
329
|
};
|
|
@@ -478,7 +495,11 @@ var _RegisterUseCase = class _RegisterUseCase {
|
|
|
478
495
|
const authEntity = mapper.map(dto, RegisterDTO, import_cca_entities3.AuthEntity);
|
|
479
496
|
const userOrAdminEntity = isAdmin ? mapper.map(dto, RegisterDTO, import_cca_entities3.AdminEntity) : mapper.map(dto, RegisterDTO, import_cca_entities3.UserEntity);
|
|
480
497
|
userOrAdminEntity.updatedAt = void 0;
|
|
481
|
-
|
|
498
|
+
if (isAdmin) {
|
|
499
|
+
authEntity.admin = userOrAdminEntity;
|
|
500
|
+
} else {
|
|
501
|
+
authEntity.user = userOrAdminEntity;
|
|
502
|
+
}
|
|
482
503
|
authEntity.password = hashedPassword;
|
|
483
504
|
authEntity.refreshToken = "";
|
|
484
505
|
return authEntity;
|
|
@@ -499,18 +520,23 @@ var _RefreshTokenUseCase = class _RefreshTokenUseCase {
|
|
|
499
520
|
}
|
|
500
521
|
async execute(refreshToken) {
|
|
501
522
|
try {
|
|
523
|
+
if (!refreshToken) return null;
|
|
502
524
|
const decoded = await this.service.verifyRefreshToken(refreshToken);
|
|
503
525
|
if (!decoded.userId) return null;
|
|
504
|
-
const authEntity = await this.repository.
|
|
526
|
+
const authEntity = await this.repository.findByUserOrAdminId(decoded.userId);
|
|
505
527
|
if (!authEntity) return null;
|
|
528
|
+
if (!authEntity.refreshToken || authEntity.refreshToken !== refreshToken) return null;
|
|
506
529
|
const account = authEntity.user ?? authEntity.admin;
|
|
507
530
|
if (!account) return null;
|
|
508
|
-
const accessToken = this.service.generateAccessToken(
|
|
531
|
+
const accessToken = this.service.generateAccessToken(
|
|
532
|
+
account,
|
|
533
|
+
authEntity.role,
|
|
534
|
+
!!authEntity.twoFactorEnabled
|
|
535
|
+
);
|
|
509
536
|
const newRefreshToken = this.service.generateRefreshToken(account);
|
|
510
537
|
await this.repository.update(authEntity.id, { refreshToken: newRefreshToken });
|
|
511
538
|
return { accessToken, refreshToken: newRefreshToken };
|
|
512
539
|
} catch (error) {
|
|
513
|
-
console.error("Refresh token failed:", error);
|
|
514
540
|
return null;
|
|
515
541
|
}
|
|
516
542
|
}
|
|
@@ -576,6 +602,9 @@ var _TwoFactorEnableUseCase = class _TwoFactorEnableUseCase {
|
|
|
576
602
|
if (!token) {
|
|
577
603
|
throw new TwoFactorError("Token is required");
|
|
578
604
|
}
|
|
605
|
+
if (!userId) {
|
|
606
|
+
throw new TwoFactorError("User ID is required");
|
|
607
|
+
}
|
|
579
608
|
const auth = await this.authRepository.findByUserOrAdminId(userId);
|
|
580
609
|
if (!auth || !auth.twoFactorSecret) {
|
|
581
610
|
throw new TwoFactorError("Please set up two-factor authentication first");
|
|
@@ -658,7 +687,7 @@ var _TwoFactorVerifyUseCase = class _TwoFactorVerifyUseCase {
|
|
|
658
687
|
if (!account) throw new NotFoundError("User or Admin account not found for AuthEntity");
|
|
659
688
|
account.lastLoginAt = /* @__PURE__ */ new Date();
|
|
660
689
|
account.isActive = true;
|
|
661
|
-
await this.authRepository.
|
|
690
|
+
await this.authRepository.saveAccount(account);
|
|
662
691
|
}
|
|
663
692
|
async updateUserRefreshToken(auth, refreshToken) {
|
|
664
693
|
auth.refreshToken = refreshToken;
|
|
@@ -668,7 +697,7 @@ var _TwoFactorVerifyUseCase = class _TwoFactorVerifyUseCase {
|
|
|
668
697
|
const account = auth.user ?? auth.admin;
|
|
669
698
|
if (!account) throw new NotFoundError("User or Admin account not found for AuthEntity");
|
|
670
699
|
return {
|
|
671
|
-
accessToken: this.jwtService.generateAccessToken(account, auth.role),
|
|
700
|
+
accessToken: this.jwtService.generateAccessToken(account, auth.role, true),
|
|
672
701
|
refreshToken: this.jwtService.generateRefreshToken(account)
|
|
673
702
|
};
|
|
674
703
|
}
|
|
@@ -690,7 +719,6 @@ var _TwoFactorDisableUseCase = class _TwoFactorDisableUseCase {
|
|
|
690
719
|
this.twoFactorService.initialize(),
|
|
691
720
|
(0, import_cca_core7.validateRepository)(this.authRepository, (repo) => repo.getAll())
|
|
692
721
|
]);
|
|
693
|
-
4;
|
|
694
722
|
this.isInitialized = true;
|
|
695
723
|
}
|
|
696
724
|
async execute(userId, dto) {
|
|
@@ -698,6 +726,12 @@ var _TwoFactorDisableUseCase = class _TwoFactorDisableUseCase {
|
|
|
698
726
|
await this.initialize();
|
|
699
727
|
}
|
|
700
728
|
const { token } = dto;
|
|
729
|
+
if (!token) {
|
|
730
|
+
throw new TwoFactorError("Token is required");
|
|
731
|
+
}
|
|
732
|
+
if (!userId) {
|
|
733
|
+
throw new TwoFactorError("User ID is required");
|
|
734
|
+
}
|
|
701
735
|
const user = await this.authRepository.findByUserOrAdminId(userId);
|
|
702
736
|
if (!user || !user.twoFactorSecret || !user.twoFactorEnabled) {
|
|
703
737
|
throw new TwoFactorError("Two-factor authentication is not enabled");
|
|
@@ -773,11 +807,13 @@ var _AuthController = class _AuthController {
|
|
|
773
807
|
}
|
|
774
808
|
const result = await this.loginUseCase.execute(loginDTO, adminPassword);
|
|
775
809
|
const adminLoginData = {
|
|
776
|
-
|
|
810
|
+
accessToken: result.accessToken,
|
|
811
|
+
userId: result.id,
|
|
812
|
+
expiresAt: result.expiresAt,
|
|
777
813
|
auth: this.createAuthData(
|
|
778
814
|
true,
|
|
779
|
-
false,
|
|
780
|
-
AUTH_STATUS.BASIC_AUTH,
|
|
815
|
+
result.enabled ?? false,
|
|
816
|
+
result.enabled ?? false ? AUTH_STATUS.PENDING_VERIFICATION : AUTH_STATUS.BASIC_AUTH,
|
|
781
817
|
false
|
|
782
818
|
)
|
|
783
819
|
};
|
|
@@ -788,7 +824,8 @@ var _AuthController = class _AuthController {
|
|
|
788
824
|
}, "adminLogin");
|
|
789
825
|
this.logout = /* @__PURE__ */ __name(async (req, res, next) => {
|
|
790
826
|
try {
|
|
791
|
-
|
|
827
|
+
const userId = req.auth?.userId ?? req.body.id;
|
|
828
|
+
await this.logoutUseCase.execute(userId);
|
|
792
829
|
const logoutData = {
|
|
793
830
|
auth: this.createAuthData(
|
|
794
831
|
false,
|
|
@@ -849,10 +886,10 @@ var _AuthController = class _AuthController {
|
|
|
849
886
|
}, "refreshToken");
|
|
850
887
|
this.setup2FA = /* @__PURE__ */ __name(async (req, res, next) => {
|
|
851
888
|
try {
|
|
852
|
-
if (!req.auth?.
|
|
889
|
+
if (!req.auth?.userId) {
|
|
853
890
|
throw new ForbiddenError("User authentication required");
|
|
854
891
|
}
|
|
855
|
-
const result = await this.twoFactorSetupUseCase.execute(req.auth.
|
|
892
|
+
const result = await this.twoFactorSetupUseCase.execute(req.auth.userId);
|
|
856
893
|
const setupData = {
|
|
857
894
|
qrCode: result.qrCodeUrl,
|
|
858
895
|
auth: this.createAuthData(true, false, AUTH_STATUS.NEEDS_SETUP)
|
|
@@ -868,7 +905,10 @@ var _AuthController = class _AuthController {
|
|
|
868
905
|
}, "setup2FA");
|
|
869
906
|
this.enable2FA = /* @__PURE__ */ __name(async (req, res, next) => {
|
|
870
907
|
try {
|
|
871
|
-
|
|
908
|
+
if (!req.auth?.userId) {
|
|
909
|
+
throw new ForbiddenError("User authentication required");
|
|
910
|
+
}
|
|
911
|
+
const dto = { ...req.body, userId: req.auth?.userId };
|
|
872
912
|
await this.twoFactorEnableUseCase.execute(dto);
|
|
873
913
|
const enableData = {
|
|
874
914
|
enabledAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -885,16 +925,22 @@ var _AuthController = class _AuthController {
|
|
|
885
925
|
}, "enable2FA");
|
|
886
926
|
this.verify2FA = /* @__PURE__ */ __name(async (req, res, next) => {
|
|
887
927
|
try {
|
|
888
|
-
|
|
928
|
+
if (!req.auth?.userId) {
|
|
929
|
+
throw new ForbiddenError("User authentication required");
|
|
930
|
+
}
|
|
931
|
+
const dto = { ...req.body, userId: req.auth.userId };
|
|
889
932
|
const result = await this.twoFactorVerifyUseCase.execute(dto);
|
|
933
|
+
if (!result) {
|
|
934
|
+
throw new Error("Two-factor verification failed");
|
|
935
|
+
}
|
|
890
936
|
const verifyData = {
|
|
891
|
-
token: result
|
|
892
|
-
refreshToken: result
|
|
937
|
+
token: result.token,
|
|
938
|
+
refreshToken: result.refreshToken,
|
|
893
939
|
user: {
|
|
894
|
-
id: result
|
|
895
|
-
email: result
|
|
896
|
-
name: result
|
|
897
|
-
role: result
|
|
940
|
+
id: result.data?.id,
|
|
941
|
+
email: result.data?.email,
|
|
942
|
+
name: result.data?.name,
|
|
943
|
+
role: result.data?.role
|
|
898
944
|
},
|
|
899
945
|
auth: this.createAuthData(true, true, AUTH_STATUS.FULL_AUTH, true)
|
|
900
946
|
};
|
|
@@ -909,7 +955,10 @@ var _AuthController = class _AuthController {
|
|
|
909
955
|
}, "verify2FA");
|
|
910
956
|
this.disable2FA = /* @__PURE__ */ __name(async (req, res, next) => {
|
|
911
957
|
try {
|
|
912
|
-
const userId = req.auth
|
|
958
|
+
const userId = req.auth?.userId;
|
|
959
|
+
if (!userId) {
|
|
960
|
+
throw new ForbiddenError("User authentication required");
|
|
961
|
+
}
|
|
913
962
|
const dto = req.body;
|
|
914
963
|
await this.twoFactorDisableUseCase.execute(userId, dto);
|
|
915
964
|
const disableData = {
|
|
@@ -973,11 +1022,14 @@ var _RequireComplete2FA = class _RequireComplete2FA {
|
|
|
973
1022
|
return res.status(401).json({ message: "Authentication required" });
|
|
974
1023
|
}
|
|
975
1024
|
const decoded = await this.jwtService.verifyAccessToken(token);
|
|
1025
|
+
if (!decoded.userId) {
|
|
1026
|
+
return res.status(401).json({ message: "Invalid token payload" });
|
|
1027
|
+
}
|
|
976
1028
|
if (!decoded.twoFactorAuthenticated) {
|
|
977
1029
|
return res.status(403).json({
|
|
978
1030
|
message: "Two-factor authentication required",
|
|
979
1031
|
code: "REQUIRE_2FA",
|
|
980
|
-
userId: decoded.
|
|
1032
|
+
userId: decoded.userId
|
|
981
1033
|
});
|
|
982
1034
|
}
|
|
983
1035
|
req.auth = { ...decoded, twoFactorAuthenticated: true };
|
|
@@ -1001,7 +1053,14 @@ var _AuthRepository = class _AuthRepository extends import_cca_core8.BaseReposit
|
|
|
1001
1053
|
return result;
|
|
1002
1054
|
}
|
|
1003
1055
|
async create(entity) {
|
|
1004
|
-
|
|
1056
|
+
const now = /* @__PURE__ */ new Date();
|
|
1057
|
+
const auth = this.repository.create({
|
|
1058
|
+
...entity,
|
|
1059
|
+
createdAt: now,
|
|
1060
|
+
updatedAt: now,
|
|
1061
|
+
isDeleted: false
|
|
1062
|
+
});
|
|
1063
|
+
return this.repository.save(auth);
|
|
1005
1064
|
}
|
|
1006
1065
|
async findByUserOrAdminId(id) {
|
|
1007
1066
|
return await this.repository.createQueryBuilder("auth").leftJoinAndSelect("auth.user", "user").leftJoinAndSelect("auth.admin", "admin").addSelect("auth.twoFactorSecret").where("user.id = :id", { id }).orWhere("admin.id = :id", { id }).getOne();
|
|
@@ -1024,6 +1083,7 @@ var _AuthRepository = class _AuthRepository extends import_cca_core8.BaseReposit
|
|
|
1024
1083
|
if (account) {
|
|
1025
1084
|
auth.refreshToken = "";
|
|
1026
1085
|
account.isActive = false;
|
|
1086
|
+
await this.saveAccount(account);
|
|
1027
1087
|
}
|
|
1028
1088
|
await this.update(auth.id, auth);
|
|
1029
1089
|
}
|
|
@@ -1058,6 +1118,9 @@ var _AuthRepository = class _AuthRepository extends import_cca_core8.BaseReposit
|
|
|
1058
1118
|
}
|
|
1059
1119
|
return auth.twoFactorSecret;
|
|
1060
1120
|
}
|
|
1121
|
+
async saveAccount(account) {
|
|
1122
|
+
await this.repository.manager.save(account);
|
|
1123
|
+
}
|
|
1061
1124
|
};
|
|
1062
1125
|
__name(_AuthRepository, "AuthRepository");
|
|
1063
1126
|
var AuthRepository = _AuthRepository;
|
|
@@ -1068,34 +1131,40 @@ var import_cca_core9 = require("cca-core");
|
|
|
1068
1131
|
var _JwtAuthService = class _JwtAuthService {
|
|
1069
1132
|
constructor(repository, config) {
|
|
1070
1133
|
this.repository = repository;
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
refreshTokenExpiry: parseInt(configData.refreshTokenExpiry, 10),
|
|
1080
|
-
...config
|
|
1081
|
-
};
|
|
1134
|
+
if (config) {
|
|
1135
|
+
this.jwtConfig = {
|
|
1136
|
+
accessTokenSecret: config.accessTokenSecret,
|
|
1137
|
+
refreshTokenSecret: config.refreshTokenSecret,
|
|
1138
|
+
accessTokenExpiry: config.accessTokenExpiry,
|
|
1139
|
+
refreshTokenExpiry: config.refreshTokenExpiry
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1082
1142
|
this.validateConfiguration();
|
|
1083
1143
|
}
|
|
1084
1144
|
async initialize() {
|
|
1085
1145
|
await (0, import_cca_core9.validateRepository)(this.repository, (repo) => repo.getAll());
|
|
1146
|
+
this.validateConfiguration();
|
|
1086
1147
|
}
|
|
1087
1148
|
validateConfiguration() {
|
|
1088
1149
|
if (!this.jwtConfig?.accessTokenSecret || !this.jwtConfig?.refreshTokenSecret) {
|
|
1089
1150
|
throw new JwtError("JWT secrets required in config");
|
|
1090
1151
|
}
|
|
1152
|
+
if (this.jwtConfig.accessTokenExpiry == null || this.jwtConfig.refreshTokenExpiry == null) {
|
|
1153
|
+
throw new JwtError("JWT expirations required in config");
|
|
1154
|
+
}
|
|
1091
1155
|
}
|
|
1092
1156
|
verifyJwtConfig() {
|
|
1093
1157
|
if (!this.jwtConfig) throw new JwtError("JWT config not loaded");
|
|
1094
1158
|
}
|
|
1095
|
-
generateAccessToken(user, role) {
|
|
1159
|
+
generateAccessToken(user, role, twoFactorAuthenticated = false) {
|
|
1096
1160
|
this.verifyJwtConfig();
|
|
1097
1161
|
return jwt.sign(
|
|
1098
|
-
{
|
|
1162
|
+
{
|
|
1163
|
+
userId: user.id,
|
|
1164
|
+
email: user.email,
|
|
1165
|
+
role,
|
|
1166
|
+
twoFactorAuthenticated
|
|
1167
|
+
},
|
|
1099
1168
|
this.jwtConfig.accessTokenSecret,
|
|
1100
1169
|
{ expiresIn: this.jwtConfig.accessTokenExpiry }
|
|
1101
1170
|
);
|
|
@@ -1110,11 +1179,8 @@ var _JwtAuthService = class _JwtAuthService {
|
|
|
1110
1179
|
}
|
|
1111
1180
|
async verifyToken(token, secret) {
|
|
1112
1181
|
try {
|
|
1113
|
-
console.log("Verifying token:", token);
|
|
1114
|
-
console.log("Using secret:", secret);
|
|
1115
1182
|
return jwt.verify(token, secret);
|
|
1116
1183
|
} catch (error) {
|
|
1117
|
-
console.error("Error verifying token:", error);
|
|
1118
1184
|
throw new UnauthorizedError();
|
|
1119
1185
|
}
|
|
1120
1186
|
}
|
|
@@ -1137,9 +1203,11 @@ var _TwoFactorService = class _TwoFactorService {
|
|
|
1137
1203
|
constructor(config) {
|
|
1138
1204
|
this.initialized = false;
|
|
1139
1205
|
this.config = config;
|
|
1206
|
+
const parsedTokenWindow = Number.parseInt(config.tokenWindow, 10);
|
|
1207
|
+
const parsedSecretLength = Number.parseInt(config.secretLength, 10);
|
|
1140
1208
|
this.twoFactorConfig = {
|
|
1141
|
-
tokenWindow:
|
|
1142
|
-
secretLength:
|
|
1209
|
+
tokenWindow: Number.isFinite(parsedTokenWindow) ? parsedTokenWindow : 1,
|
|
1210
|
+
secretLength: Number.isFinite(parsedSecretLength) ? parsedSecretLength : 20,
|
|
1143
1211
|
qrCodeOptions: {
|
|
1144
1212
|
errorCorrectionLevel: "M",
|
|
1145
1213
|
margin: 4,
|
|
@@ -1228,25 +1296,35 @@ async function createAuthContainer(database) {
|
|
|
1228
1296
|
database.getRepository(import_cca_entities5.AuthEntity)
|
|
1229
1297
|
);
|
|
1230
1298
|
container.registerRepository("AuthRepository", authRepository);
|
|
1231
|
-
const jwtAuthService = new JwtAuthService(authRepository);
|
|
1232
|
-
container.registerService("JwtAuthService", jwtAuthService);
|
|
1233
1299
|
const configData = await createConfigInstance();
|
|
1300
|
+
const parseExpiry = /* @__PURE__ */ __name((value) => {
|
|
1301
|
+
const numeric = Number(value);
|
|
1302
|
+
return Number.isFinite(numeric) ? numeric : value;
|
|
1303
|
+
}, "parseExpiry");
|
|
1304
|
+
const jwtConfig = {
|
|
1305
|
+
accessTokenSecret: configData.accessTokenSecret,
|
|
1306
|
+
refreshTokenSecret: configData.refreshTokenSecret,
|
|
1307
|
+
accessTokenExpiry: parseExpiry(configData.accessTokenExpiry),
|
|
1308
|
+
refreshTokenExpiry: parseExpiry(configData.refreshTokenExpiry)
|
|
1309
|
+
};
|
|
1310
|
+
const configuredJwtAuthService = new JwtAuthService(authRepository, jwtConfig);
|
|
1311
|
+
container.registerService("JwtAuthService", configuredJwtAuthService);
|
|
1234
1312
|
const twoFactorService = new TwoFactorService(configData);
|
|
1235
1313
|
container.registerService("TwoFactorService", twoFactorService);
|
|
1236
|
-
const requireComplete2FA = new RequireComplete2FA(
|
|
1237
|
-
const loginUseCase = new LoginUseCase(authRepository,
|
|
1314
|
+
const requireComplete2FA = new RequireComplete2FA(configuredJwtAuthService);
|
|
1315
|
+
const loginUseCase = new LoginUseCase(authRepository, configuredJwtAuthService);
|
|
1238
1316
|
const logoutUseCase = new LogoutUseCase(authRepository);
|
|
1239
1317
|
const registerUseCase = new RegisterUseCase(authRepository);
|
|
1240
1318
|
const refreshTokenUseCase = new RefreshTokenUseCase(
|
|
1241
1319
|
authRepository,
|
|
1242
|
-
|
|
1320
|
+
configuredJwtAuthService
|
|
1243
1321
|
);
|
|
1244
1322
|
const twoFactorSetupUseCase = new TwoFactorSetupUseCase(twoFactorService, authRepository);
|
|
1245
1323
|
const twoFactorEnableUseCase = new TwoFactorEnableUseCase(twoFactorService, authRepository);
|
|
1246
1324
|
const twoFactorVerifyUseCase = new TwoFactorVerifyUseCase(
|
|
1247
1325
|
twoFactorService,
|
|
1248
1326
|
authRepository,
|
|
1249
|
-
|
|
1327
|
+
configuredJwtAuthService
|
|
1250
1328
|
);
|
|
1251
1329
|
const twoFactorDisableUseCase = new TwoFactorDisableUseCase(twoFactorService, authRepository);
|
|
1252
1330
|
container.registerService("LoginUseCase", loginUseCase);
|