@flusys/nestjs-auth 3.0.1 → 4.0.0-lts
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/cjs/config/index.js +1 -0
- package/cjs/config/message-keys.js +150 -0
- package/cjs/controllers/authentication.controller.js +9 -4
- package/cjs/controllers/branch.controller.js +1 -0
- package/cjs/controllers/company-selection.controller.js +3 -0
- package/cjs/controllers/company.controller.js +1 -0
- package/cjs/controllers/email-verification.controller.js +7 -2
- package/cjs/controllers/user-permission.controller.js +9 -1
- package/cjs/controllers/user.controller.js +16 -4
- package/cjs/dtos/authentication.dto.js +8 -0
- package/cjs/dtos/user-permission.dto.js +14 -0
- package/cjs/interceptors/set-token.interceptor.js +14 -5
- package/cjs/services/auth-config.service.js +9 -8
- package/cjs/services/auth-datasource.provider.js +7 -4
- package/cjs/services/auth-email.service.js +155 -45
- package/cjs/services/authentication.service.js +274 -60
- package/cjs/services/branch.service.js +1 -1
- package/cjs/services/company-selection-session.service.js +25 -6
- package/cjs/services/company.service.js +1 -1
- package/cjs/services/user-permission.service.js +52 -4
- package/cjs/services/user.service.js +122 -26
- package/cjs/strategies/jwt.strategy.js +18 -4
- package/config/index.d.ts +1 -0
- package/config/message-keys.d.ts +218 -0
- package/controllers/company-selection.controller.d.ts +2 -0
- package/controllers/user-permission.controller.d.ts +4 -0
- package/controllers/user.controller.d.ts +10 -0
- package/dtos/authentication.dto.d.ts +1 -0
- package/dtos/user-permission.dto.d.ts +2 -0
- package/entities/index.d.ts +10 -1
- package/fesm/config/index.js +1 -0
- package/fesm/config/message-keys.js +118 -0
- package/fesm/controllers/authentication.controller.js +9 -3
- package/fesm/controllers/branch.controller.js +1 -0
- package/fesm/controllers/company-selection.controller.js +3 -0
- package/fesm/controllers/company.controller.js +1 -0
- package/fesm/controllers/email-verification.controller.js +7 -2
- package/fesm/controllers/user-permission.controller.js +9 -1
- package/fesm/controllers/user.controller.js +16 -4
- package/fesm/dtos/authentication.dto.js +8 -0
- package/fesm/dtos/user-permission.dto.js +14 -0
- package/fesm/interceptors/set-token.interceptor.js +14 -4
- package/fesm/services/auth-config.service.js +10 -9
- package/fesm/services/auth-datasource.provider.js +7 -4
- package/fesm/services/auth-email.service.js +156 -45
- package/fesm/services/authentication.service.js +276 -61
- package/fesm/services/branch.service.js +1 -1
- package/fesm/services/company-selection-session.service.js +25 -6
- package/fesm/services/company.service.js +1 -1
- package/fesm/services/user-permission.service.js +52 -4
- package/fesm/services/user.service.js +122 -25
- package/fesm/strategies/jwt.strategy.js +18 -4
- package/interfaces/auth-module-options.interface.d.ts +2 -2
- package/interfaces/authentication.interface.d.ts +1 -0
- package/modules/auth.module.d.ts +2 -1
- package/package.json +6 -4
- package/services/auth-config.service.d.ts +0 -2
- package/services/auth-datasource.provider.d.ts +1 -3
- package/services/auth-email.service.d.ts +6 -3
- package/services/authentication.service.d.ts +1 -1
- package/services/branch.service.d.ts +2 -1
- package/services/user.service.d.ts +7 -7
|
@@ -25,15 +25,14 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
+
import { LogAction } from '@flusys/nestjs-shared';
|
|
28
29
|
import { HybridCache } from '@flusys/nestjs-shared/classes';
|
|
29
30
|
import { Inject, Injectable } from '@nestjs/common';
|
|
30
31
|
import { randomUUID } from 'crypto';
|
|
31
32
|
import { SESSION_CACHE_PREFIX } from '../config/auth.constants';
|
|
32
33
|
const SESSION_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
33
34
|
export class CompanySelectionSessionService {
|
|
34
|
-
|
|
35
|
-
* Create a new session for company/branch selection
|
|
36
|
-
*/ async createSession(userId) {
|
|
35
|
+
async createSession(userId) {
|
|
37
36
|
const sessionId = randomUUID();
|
|
38
37
|
const now = new Date();
|
|
39
38
|
const expiresAt = new Date(now.getTime() + SESSION_TTL_MS);
|
|
@@ -58,9 +57,7 @@ export class CompanySelectionSessionService {
|
|
|
58
57
|
}
|
|
59
58
|
return session;
|
|
60
59
|
}
|
|
61
|
-
|
|
62
|
-
* Get and delete session (one-time use)
|
|
63
|
-
*/ async consumeSession(sessionId) {
|
|
60
|
+
async consumeSession(sessionId) {
|
|
64
61
|
const session = await this.getSession(sessionId);
|
|
65
62
|
if (session) {
|
|
66
63
|
await this.deleteSession(sessionId);
|
|
@@ -82,6 +79,28 @@ export class CompanySelectionSessionService {
|
|
|
82
79
|
this.cacheManager = cacheManager;
|
|
83
80
|
}
|
|
84
81
|
}
|
|
82
|
+
_ts_decorate([
|
|
83
|
+
LogAction({
|
|
84
|
+
action: 'session.create',
|
|
85
|
+
module: 'auth'
|
|
86
|
+
}),
|
|
87
|
+
_ts_metadata("design:type", Function),
|
|
88
|
+
_ts_metadata("design:paramtypes", [
|
|
89
|
+
String
|
|
90
|
+
]),
|
|
91
|
+
_ts_metadata("design:returntype", Promise)
|
|
92
|
+
], CompanySelectionSessionService.prototype, "createSession", null);
|
|
93
|
+
_ts_decorate([
|
|
94
|
+
LogAction({
|
|
95
|
+
action: 'session.consume',
|
|
96
|
+
module: 'auth'
|
|
97
|
+
}),
|
|
98
|
+
_ts_metadata("design:type", Function),
|
|
99
|
+
_ts_metadata("design:paramtypes", [
|
|
100
|
+
String
|
|
101
|
+
]),
|
|
102
|
+
_ts_metadata("design:returntype", Promise)
|
|
103
|
+
], CompanySelectionSessionService.prototype, "consumeSession", null);
|
|
85
104
|
CompanySelectionSessionService = _ts_decorate([
|
|
86
105
|
Injectable(),
|
|
87
106
|
_ts_param(0, Inject('CACHE_INSTANCE')),
|
|
@@ -64,7 +64,7 @@ export class CompanyService extends RequestScopedApiService {
|
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
constructor(cacheManager, utilsService, dataSourceProvider){
|
|
67
|
-
super('company', null, cacheManager, utilsService, CompanyService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider;
|
|
67
|
+
super('company', null, cacheManager, utilsService, CompanyService.name, true, 'auth'), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider;
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
CompanyService = _ts_decorate([
|
|
@@ -25,7 +25,9 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
+
import { LogAction } from '@flusys/nestjs-shared';
|
|
28
29
|
import { ConflictException, Inject, Injectable, NotFoundException, Scope } from '@nestjs/common';
|
|
30
|
+
import { USER_PERMISSION_MESSAGES } from '../config';
|
|
29
31
|
import { PermissionType, UserCompanyPermission } from '../entities/user-company-permission.entity';
|
|
30
32
|
import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
31
33
|
export class UserPermissionService {
|
|
@@ -62,7 +64,13 @@ export class UserPermissionService {
|
|
|
62
64
|
}
|
|
63
65
|
});
|
|
64
66
|
if (existing) {
|
|
65
|
-
throw new ConflictException(
|
|
67
|
+
throw new ConflictException({
|
|
68
|
+
message: `User already assigned to this ${typeName}`,
|
|
69
|
+
messageKey: USER_PERMISSION_MESSAGES.ALREADY_ASSIGNED,
|
|
70
|
+
messageParams: {
|
|
71
|
+
type: typeName
|
|
72
|
+
}
|
|
73
|
+
});
|
|
66
74
|
}
|
|
67
75
|
try {
|
|
68
76
|
await repository.save(repository.create({
|
|
@@ -72,8 +80,15 @@ export class UserPermissionService {
|
|
|
72
80
|
metadata
|
|
73
81
|
}));
|
|
74
82
|
} catch (error) {
|
|
75
|
-
|
|
76
|
-
|
|
83
|
+
const err = error;
|
|
84
|
+
if (err?.code === 'ER_DUP_ENTRY' || String(err?.message).includes('Duplicate entry')) {
|
|
85
|
+
throw new ConflictException({
|
|
86
|
+
message: `User already assigned to this ${typeName}. Please refresh and try again.`,
|
|
87
|
+
messageKey: USER_PERMISSION_MESSAGES.ALREADY_ASSIGNED,
|
|
88
|
+
messageParams: {
|
|
89
|
+
type: typeName
|
|
90
|
+
}
|
|
91
|
+
});
|
|
77
92
|
}
|
|
78
93
|
throw error;
|
|
79
94
|
}
|
|
@@ -89,7 +104,13 @@ export class UserPermissionService {
|
|
|
89
104
|
}
|
|
90
105
|
});
|
|
91
106
|
if (!permission) {
|
|
92
|
-
throw new NotFoundException(
|
|
107
|
+
throw new NotFoundException({
|
|
108
|
+
message: `${typeName.charAt(0).toUpperCase() + typeName.slice(1)} permission not found`,
|
|
109
|
+
messageKey: USER_PERMISSION_MESSAGES.PERMISSION_NOT_FOUND,
|
|
110
|
+
messageParams: {
|
|
111
|
+
type: typeName.charAt(0).toUpperCase() + typeName.slice(1)
|
|
112
|
+
}
|
|
113
|
+
});
|
|
93
114
|
}
|
|
94
115
|
await repository.remove(permission);
|
|
95
116
|
}
|
|
@@ -117,6 +138,33 @@ export class UserPermissionService {
|
|
|
117
138
|
this.findUserCompanies = (userId)=>this.findUserPermissions(userId, PermissionType.COMPANY);
|
|
118
139
|
}
|
|
119
140
|
}
|
|
141
|
+
_ts_decorate([
|
|
142
|
+
LogAction({
|
|
143
|
+
action: 'user.assignPermission',
|
|
144
|
+
module: 'auth'
|
|
145
|
+
}),
|
|
146
|
+
_ts_metadata("design:type", Function),
|
|
147
|
+
_ts_metadata("design:paramtypes", [
|
|
148
|
+
String,
|
|
149
|
+
String,
|
|
150
|
+
typeof PermissionType === "undefined" ? Object : PermissionType,
|
|
151
|
+
typeof Record === "undefined" ? Object : Record
|
|
152
|
+
]),
|
|
153
|
+
_ts_metadata("design:returntype", Promise)
|
|
154
|
+
], UserPermissionService.prototype, "assignPermission", null);
|
|
155
|
+
_ts_decorate([
|
|
156
|
+
LogAction({
|
|
157
|
+
action: 'user.revokePermission',
|
|
158
|
+
module: 'auth'
|
|
159
|
+
}),
|
|
160
|
+
_ts_metadata("design:type", Function),
|
|
161
|
+
_ts_metadata("design:paramtypes", [
|
|
162
|
+
String,
|
|
163
|
+
String,
|
|
164
|
+
typeof PermissionType === "undefined" ? Object : PermissionType
|
|
165
|
+
]),
|
|
166
|
+
_ts_metadata("design:returntype", Promise)
|
|
167
|
+
], UserPermissionService.prototype, "revokePermission", null);
|
|
120
168
|
UserPermissionService = _ts_decorate([
|
|
121
169
|
Injectable({
|
|
122
170
|
scope: Scope.REQUEST
|
|
@@ -26,13 +26,17 @@ function _ts_param(paramIndex, decorator) {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
import { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
|
|
29
|
+
import { LogAction } from '@flusys/nestjs-shared';
|
|
30
|
+
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
29
31
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
30
|
-
import { ErrorHandler } from '@flusys/nestjs-shared/utils';
|
|
31
32
|
import { BadRequestException, ForbiddenException, Inject, Injectable, NotFoundException, Optional, Scope } from '@nestjs/common';
|
|
33
|
+
import { SYSTEM_MESSAGES } from '@flusys/nestjs-shared/constants';
|
|
34
|
+
import { AUTH_MESSAGES, USER_MESSAGES, USER_PERMISSION_MESSAGES } from '../config';
|
|
32
35
|
import * as bcrypt from 'bcrypt';
|
|
33
36
|
import { AuthConfigService } from './auth-config.service';
|
|
34
37
|
import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
35
38
|
import { BCRYPT_SALT_ROUNDS, USER_ENRICHER } from '../config';
|
|
39
|
+
import { CreateUserDto } from '../dtos/user.dto';
|
|
36
40
|
import { PermissionType } from '../enums';
|
|
37
41
|
import { IUserEnricher } from '../interfaces/user-enricher.interface';
|
|
38
42
|
import { UserCompanyPermission } from '../entities/user-company-permission.entity';
|
|
@@ -58,7 +62,10 @@ export class UserService extends RequestScopedApiService {
|
|
|
58
62
|
}
|
|
59
63
|
async getUserForProfile(userId, user) {
|
|
60
64
|
if (userId !== user.id) {
|
|
61
|
-
throw new ForbiddenException(
|
|
65
|
+
throw new ForbiddenException({
|
|
66
|
+
message: 'Cannot access another user\'s profile',
|
|
67
|
+
messageKey: AUTH_MESSAGES.PROFILE_ACCESS_DENIED
|
|
68
|
+
});
|
|
62
69
|
}
|
|
63
70
|
await this.ensureRepositoryInitialized();
|
|
64
71
|
const userEntity = await this.repository.findOne({
|
|
@@ -67,7 +74,10 @@ export class UserService extends RequestScopedApiService {
|
|
|
67
74
|
}
|
|
68
75
|
});
|
|
69
76
|
if (!userEntity) {
|
|
70
|
-
throw new NotFoundException(
|
|
77
|
+
throw new NotFoundException({
|
|
78
|
+
message: 'User not found',
|
|
79
|
+
messageKey: USER_MESSAGES.NOT_FOUND
|
|
80
|
+
});
|
|
71
81
|
}
|
|
72
82
|
return userEntity;
|
|
73
83
|
}
|
|
@@ -105,7 +115,10 @@ export class UserService extends RequestScopedApiService {
|
|
|
105
115
|
}
|
|
106
116
|
});
|
|
107
117
|
if (!permission) {
|
|
108
|
-
throw new NotFoundException(
|
|
118
|
+
throw new NotFoundException({
|
|
119
|
+
message: 'User company permission not found',
|
|
120
|
+
messageKey: USER_PERMISSION_MESSAGES.PERMISSION_NOT_FOUND
|
|
121
|
+
});
|
|
109
122
|
}
|
|
110
123
|
permission.isActive = isActive;
|
|
111
124
|
await this.permissionRepository.save(permission);
|
|
@@ -235,7 +248,7 @@ export class UserService extends RequestScopedApiService {
|
|
|
235
248
|
async insert(dto, user) {
|
|
236
249
|
const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
|
|
237
250
|
if (!isCompanyFeatureEnabled || !this.dataSource || !user?.companyId || !this.permissionRepository) {
|
|
238
|
-
return super.insert(dto, user);
|
|
251
|
+
return await super.insert(dto, user);
|
|
239
252
|
}
|
|
240
253
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
241
254
|
await queryRunner.connect();
|
|
@@ -284,10 +297,7 @@ export class UserService extends RequestScopedApiService {
|
|
|
284
297
|
return this.convertEntityToResponseDto(savedUser, false);
|
|
285
298
|
} catch (error) {
|
|
286
299
|
await queryRunner.rollbackTransaction();
|
|
287
|
-
|
|
288
|
-
entity: this.entityName
|
|
289
|
-
});
|
|
290
|
-
ErrorHandler.rethrowError(error);
|
|
300
|
+
throw error;
|
|
291
301
|
} finally{
|
|
292
302
|
await queryRunner.release();
|
|
293
303
|
}
|
|
@@ -334,10 +344,16 @@ export class UserService extends RequestScopedApiService {
|
|
|
334
344
|
async updateProfileSection(userId, sectionId, data, user) {
|
|
335
345
|
const userEntity = await this.getUserForProfile(userId, user);
|
|
336
346
|
if (!this.dataSource) {
|
|
337
|
-
throw new BadRequestException(
|
|
347
|
+
throw new BadRequestException({
|
|
348
|
+
message: 'DataSource not available',
|
|
349
|
+
messageKey: SYSTEM_MESSAGES.DATASOURCE_NOT_AVAILABLE
|
|
350
|
+
});
|
|
338
351
|
}
|
|
339
352
|
if (!this.userEnricher?.updateProfileSection) {
|
|
340
|
-
throw new BadRequestException(
|
|
353
|
+
throw new BadRequestException({
|
|
354
|
+
message: 'Profile sections not supported',
|
|
355
|
+
messageKey: AUTH_MESSAGES.PROFILE_SECTIONS_NOT_SUPPORTED
|
|
356
|
+
});
|
|
341
357
|
}
|
|
342
358
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
343
359
|
await queryRunner.connect();
|
|
@@ -350,11 +366,7 @@ export class UserService extends RequestScopedApiService {
|
|
|
350
366
|
]);
|
|
351
367
|
} catch (error) {
|
|
352
368
|
await queryRunner.rollbackTransaction();
|
|
353
|
-
|
|
354
|
-
entity: this.entityName,
|
|
355
|
-
sectionId
|
|
356
|
-
});
|
|
357
|
-
ErrorHandler.rethrowError(error);
|
|
369
|
+
throw error;
|
|
358
370
|
} finally{
|
|
359
371
|
await queryRunner.release();
|
|
360
372
|
}
|
|
@@ -369,7 +381,10 @@ export class UserService extends RequestScopedApiService {
|
|
|
369
381
|
async profile(dto, user) {
|
|
370
382
|
await this.ensureRepositoryInitialized();
|
|
371
383
|
if (!this.dataSource) {
|
|
372
|
-
throw new BadRequestException(
|
|
384
|
+
throw new BadRequestException({
|
|
385
|
+
message: 'DataSource not available',
|
|
386
|
+
messageKey: SYSTEM_MESSAGES.DATASOURCE_NOT_AVAILABLE
|
|
387
|
+
});
|
|
373
388
|
}
|
|
374
389
|
const userId = dto.id ?? user.id;
|
|
375
390
|
if (dto.extras && this.userEnricher?.validateProfileExtras) {
|
|
@@ -385,17 +400,26 @@ export class UserService extends RequestScopedApiService {
|
|
|
385
400
|
}
|
|
386
401
|
});
|
|
387
402
|
if (!findUser) {
|
|
388
|
-
throw new NotFoundException(
|
|
403
|
+
throw new NotFoundException({
|
|
404
|
+
message: 'User not found',
|
|
405
|
+
messageKey: USER_MESSAGES.NOT_FOUND
|
|
406
|
+
});
|
|
389
407
|
}
|
|
390
408
|
if (dto.password) {
|
|
391
409
|
const isOwnProfile = !dto.id || dto.id === user.id;
|
|
392
410
|
if (isOwnProfile) {
|
|
393
411
|
if (!dto.oldPassword) {
|
|
394
|
-
throw new BadRequestException(
|
|
412
|
+
throw new BadRequestException({
|
|
413
|
+
message: 'Current password is required to change your password',
|
|
414
|
+
messageKey: AUTH_MESSAGES.PASSWORD_CHANGE_CURRENT_REQUIRED
|
|
415
|
+
});
|
|
395
416
|
}
|
|
396
417
|
const isPasswordValid = await bcrypt.compare(dto.oldPassword, findUser.password);
|
|
397
418
|
if (!isPasswordValid) {
|
|
398
|
-
throw new BadRequestException(
|
|
419
|
+
throw new BadRequestException({
|
|
420
|
+
message: 'Current password is incorrect',
|
|
421
|
+
messageKey: AUTH_MESSAGES.PASSWORD_CHANGE_INVALID_CURRENT
|
|
422
|
+
});
|
|
399
423
|
}
|
|
400
424
|
}
|
|
401
425
|
findUser.password = await bcrypt.hash(dto.password, BCRYPT_SALT_ROUNDS);
|
|
@@ -413,18 +437,91 @@ export class UserService extends RequestScopedApiService {
|
|
|
413
437
|
return findUser.id;
|
|
414
438
|
} catch (error) {
|
|
415
439
|
await queryRunner.rollbackTransaction();
|
|
416
|
-
|
|
417
|
-
entity: this.entityName
|
|
418
|
-
});
|
|
419
|
-
ErrorHandler.rethrowError(error);
|
|
440
|
+
throw error;
|
|
420
441
|
} finally{
|
|
421
442
|
await queryRunner.release();
|
|
422
443
|
}
|
|
423
444
|
}
|
|
424
445
|
constructor(cacheManager, utilsService, dataSourceProvider, authConfig, userEnricher){
|
|
425
|
-
super('user', null, cacheManager, utilsService, UserService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), _define_property(this, "authConfig", void 0), _define_property(this, "userEnricher", void 0), _define_property(this, "permissionRepository", void 0), _define_property(this, "dataSource", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider, this.authConfig = authConfig, this.userEnricher = userEnricher;
|
|
446
|
+
super('user', null, cacheManager, utilsService, UserService.name, true, 'auth'), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), _define_property(this, "authConfig", void 0), _define_property(this, "userEnricher", void 0), _define_property(this, "permissionRepository", void 0), _define_property(this, "dataSource", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider, this.authConfig = authConfig, this.userEnricher = userEnricher;
|
|
426
447
|
}
|
|
427
448
|
}
|
|
449
|
+
_ts_decorate([
|
|
450
|
+
LogAction({
|
|
451
|
+
action: 'user.verifyEmail',
|
|
452
|
+
module: 'auth'
|
|
453
|
+
}),
|
|
454
|
+
_ts_metadata("design:type", Function),
|
|
455
|
+
_ts_metadata("design:paramtypes", [
|
|
456
|
+
String
|
|
457
|
+
]),
|
|
458
|
+
_ts_metadata("design:returntype", Promise)
|
|
459
|
+
], UserService.prototype, "verifyEmail", null);
|
|
460
|
+
_ts_decorate([
|
|
461
|
+
LogAction({
|
|
462
|
+
action: 'user.verifyPhone',
|
|
463
|
+
module: 'auth'
|
|
464
|
+
}),
|
|
465
|
+
_ts_metadata("design:type", Function),
|
|
466
|
+
_ts_metadata("design:paramtypes", [
|
|
467
|
+
String
|
|
468
|
+
]),
|
|
469
|
+
_ts_metadata("design:returntype", Promise)
|
|
470
|
+
], UserService.prototype, "verifyPhone", null);
|
|
471
|
+
_ts_decorate([
|
|
472
|
+
LogAction({
|
|
473
|
+
action: 'user.updateStatus',
|
|
474
|
+
module: 'auth'
|
|
475
|
+
}),
|
|
476
|
+
_ts_metadata("design:type", Function),
|
|
477
|
+
_ts_metadata("design:paramtypes", [
|
|
478
|
+
String,
|
|
479
|
+
Boolean,
|
|
480
|
+
Object
|
|
481
|
+
]),
|
|
482
|
+
_ts_metadata("design:returntype", Promise)
|
|
483
|
+
], UserService.prototype, "updateStatus", null);
|
|
484
|
+
_ts_decorate([
|
|
485
|
+
LogAction({
|
|
486
|
+
action: 'user.create',
|
|
487
|
+
module: 'auth',
|
|
488
|
+
includeParams: false
|
|
489
|
+
}),
|
|
490
|
+
_ts_metadata("design:type", Function),
|
|
491
|
+
_ts_metadata("design:paramtypes", [
|
|
492
|
+
typeof CreateUserDto === "undefined" ? Object : CreateUserDto,
|
|
493
|
+
Object
|
|
494
|
+
]),
|
|
495
|
+
_ts_metadata("design:returntype", Promise)
|
|
496
|
+
], UserService.prototype, "insert", null);
|
|
497
|
+
_ts_decorate([
|
|
498
|
+
LogAction({
|
|
499
|
+
action: 'user.updateProfileSection',
|
|
500
|
+
module: 'auth',
|
|
501
|
+
includeParams: false
|
|
502
|
+
}),
|
|
503
|
+
_ts_metadata("design:type", Function),
|
|
504
|
+
_ts_metadata("design:paramtypes", [
|
|
505
|
+
String,
|
|
506
|
+
String,
|
|
507
|
+
typeof Record === "undefined" ? Object : Record,
|
|
508
|
+
typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
|
|
509
|
+
]),
|
|
510
|
+
_ts_metadata("design:returntype", Promise)
|
|
511
|
+
], UserService.prototype, "updateProfileSection", null);
|
|
512
|
+
_ts_decorate([
|
|
513
|
+
LogAction({
|
|
514
|
+
action: 'user.updateProfile',
|
|
515
|
+
module: 'auth',
|
|
516
|
+
includeParams: false
|
|
517
|
+
}),
|
|
518
|
+
_ts_metadata("design:type", Function),
|
|
519
|
+
_ts_metadata("design:paramtypes", [
|
|
520
|
+
Object,
|
|
521
|
+
typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
|
|
522
|
+
]),
|
|
523
|
+
_ts_metadata("design:returntype", Promise)
|
|
524
|
+
], UserService.prototype, "profile", null);
|
|
428
525
|
UserService = _ts_decorate([
|
|
429
526
|
Injectable({
|
|
430
527
|
scope: Scope.REQUEST
|
|
@@ -20,7 +20,9 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
20
20
|
function _ts_metadata(k, v) {
|
|
21
21
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
22
|
}
|
|
23
|
+
import { SYSTEM_MESSAGES } from '@flusys/nestjs-shared/constants';
|
|
23
24
|
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
|
25
|
+
import { AUTH_MESSAGES, USER_MESSAGES } from '../config';
|
|
24
26
|
import { ModuleRef } from '@nestjs/core';
|
|
25
27
|
import { PassportStrategy } from '@nestjs/passport';
|
|
26
28
|
import { ExtractJwt, Strategy } from 'passport-jwt';
|
|
@@ -42,7 +44,10 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
|
|
42
44
|
}
|
|
43
45
|
});
|
|
44
46
|
if (!user) {
|
|
45
|
-
throw new UnauthorizedException(
|
|
47
|
+
throw new UnauthorizedException({
|
|
48
|
+
message: 'User not found or inactive',
|
|
49
|
+
messageKey: USER_MESSAGES.NOT_FOUND
|
|
50
|
+
});
|
|
46
51
|
}
|
|
47
52
|
return this.buildUserInfo(payload);
|
|
48
53
|
}
|
|
@@ -55,9 +60,15 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
|
|
55
60
|
return provider.getRepository(User);
|
|
56
61
|
}
|
|
57
62
|
} catch {
|
|
58
|
-
throw new UnauthorizedException(
|
|
63
|
+
throw new UnauthorizedException({
|
|
64
|
+
message: 'Unable to access user data',
|
|
65
|
+
messageKey: SYSTEM_MESSAGES.SERVICE_NOT_AVAILABLE
|
|
66
|
+
});
|
|
59
67
|
}
|
|
60
|
-
throw new UnauthorizedException(
|
|
68
|
+
throw new UnauthorizedException({
|
|
69
|
+
message: 'User repository not available',
|
|
70
|
+
messageKey: SYSTEM_MESSAGES.REPOSITORY_NOT_AVAILABLE
|
|
71
|
+
});
|
|
61
72
|
}
|
|
62
73
|
async checkTokenRevocation(payload) {
|
|
63
74
|
if (!payload.iat) return;
|
|
@@ -66,7 +77,10 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
|
|
66
77
|
strict: false
|
|
67
78
|
});
|
|
68
79
|
if (authService && await authService.isTokenRevoked(payload.id, payload.iat)) {
|
|
69
|
-
throw new UnauthorizedException(
|
|
80
|
+
throw new UnauthorizedException({
|
|
81
|
+
message: 'Token has been revoked',
|
|
82
|
+
messageKey: AUTH_MESSAGES.TOKEN_REVOKED
|
|
83
|
+
});
|
|
70
84
|
}
|
|
71
85
|
} catch (err) {
|
|
72
86
|
if (err instanceof UnauthorizedException) throw err;
|
|
@@ -19,8 +19,8 @@ export interface AuthOptionsFactory extends IModuleOptionsFactory<IAuthModuleCon
|
|
|
19
19
|
}
|
|
20
20
|
export interface AuthModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'>, IDynamicModuleConfig {
|
|
21
21
|
bootstrapAppConfig: IBootstrapAppConfig;
|
|
22
|
-
useFactory?: (...args:
|
|
23
|
-
inject?:
|
|
22
|
+
useFactory?: (...args: unknown[]) => Promise<IAuthModuleConfig> | IAuthModuleConfig;
|
|
23
|
+
inject?: unknown[];
|
|
24
24
|
useClass?: Type<AuthOptionsFactory>;
|
|
25
25
|
useExisting?: Type<AuthOptionsFactory>;
|
|
26
26
|
providers?: Provider[];
|
package/modules/auth.module.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IBootstrapAppConfig } from '@flusys/nestjs-core';
|
|
2
2
|
import { DynamicModule } from '@nestjs/common';
|
|
3
|
+
import { getEntitiesByConfig } from '../entities';
|
|
3
4
|
import { AuthModuleAsyncOptions, AuthModuleOptions } from '../interfaces/auth-module-options.interface';
|
|
4
5
|
export declare class AuthModule {
|
|
5
6
|
static forRoot(options: AuthModuleOptions): DynamicModule;
|
|
@@ -11,5 +12,5 @@ export declare class AuthModule {
|
|
|
11
12
|
private static buildProviders;
|
|
12
13
|
private static buildExports;
|
|
13
14
|
private static createAsyncProviders;
|
|
14
|
-
static getEntities(config: IBootstrapAppConfig):
|
|
15
|
+
static getEntities(config: IBootstrapAppConfig): ReturnType<typeof getEntitiesByConfig>;
|
|
15
16
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flusys/nestjs-auth",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0-lts",
|
|
4
4
|
"description": "Authentication and authorization infrastructure for NestJS applications",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "fesm/index.js",
|
|
@@ -88,10 +88,12 @@
|
|
|
88
88
|
"passport": "^0.7.0",
|
|
89
89
|
"passport-jwt": "^4.0.0",
|
|
90
90
|
"passport-local": "^1.0.0",
|
|
91
|
-
"typeorm": "^0.3.0"
|
|
91
|
+
"typeorm": "^0.3.0",
|
|
92
|
+
"express": "^4.18.0",
|
|
93
|
+
"rxjs": "^7.8.0"
|
|
92
94
|
},
|
|
93
95
|
"dependencies": {
|
|
94
|
-
"@flusys/nestjs-core": "
|
|
95
|
-
"@flusys/nestjs-shared": "
|
|
96
|
+
"@flusys/nestjs-core": "4.0.0-lts",
|
|
97
|
+
"@flusys/nestjs-shared": "4.0.0-lts"
|
|
96
98
|
}
|
|
97
99
|
}
|
|
@@ -2,10 +2,8 @@ import { DatabaseMode } from '@flusys/nestjs-core';
|
|
|
2
2
|
import { IModuleConfigService } from '@flusys/nestjs-shared/interfaces';
|
|
3
3
|
import { AuthModuleOptions } from '../interfaces/auth-module-options.interface';
|
|
4
4
|
export declare class AuthConfigService implements IModuleConfigService {
|
|
5
|
-
private readonly logger;
|
|
6
5
|
private readonly options;
|
|
7
6
|
private readonly config;
|
|
8
|
-
private refreshTokenSecretWarned;
|
|
9
7
|
constructor(injectedOptions?: AuthModuleOptions);
|
|
10
8
|
getDatabaseMode(): DatabaseMode;
|
|
11
9
|
isMultiTenant(): boolean;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { MultiTenantDataSourceService } from '@flusys/nestjs-shared
|
|
1
|
+
import { MultiTenantDataSourceService } from '@flusys/nestjs-shared';
|
|
2
2
|
import { IDatabaseConfig, ITenantDatabaseConfig } from '@flusys/nestjs-core';
|
|
3
|
-
import { Logger } from '@nestjs/common';
|
|
4
3
|
import { Request } from 'express';
|
|
5
4
|
import { DataSource } from 'typeorm';
|
|
6
5
|
import { AuthModuleOptions } from '../interfaces/auth-module-options.interface';
|
|
7
6
|
export declare class AuthDataSourceProvider extends MultiTenantDataSourceService {
|
|
8
7
|
private readonly authOptions;
|
|
9
|
-
protected readonly logger: Logger;
|
|
10
8
|
protected static readonly tenantConnections: Map<string, DataSource>;
|
|
11
9
|
protected static singleDataSource: DataSource | null;
|
|
12
10
|
protected static readonly tenantsRegistry: Map<string, ITenantDatabaseConfig>;
|
|
@@ -4,7 +4,6 @@ import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
|
4
4
|
export declare class AuthEmailService {
|
|
5
5
|
private readonly dataSourceProvider?;
|
|
6
6
|
private readonly emailProvider?;
|
|
7
|
-
private readonly logger;
|
|
8
7
|
private readonly PASSWORD_RESET_EXPIRY;
|
|
9
8
|
private readonly EMAIL_VERIFICATION_EXPIRY;
|
|
10
9
|
constructor(dataSourceProvider?: AuthDataSourceProvider, emailProvider?: IAuthEmailProvider);
|
|
@@ -12,7 +11,6 @@ export declare class AuthEmailService {
|
|
|
12
11
|
private getRepository;
|
|
13
12
|
private generateToken;
|
|
14
13
|
private hashToken;
|
|
15
|
-
private getErrorMessage;
|
|
16
14
|
private assertEmailEnabled;
|
|
17
15
|
private sendVerificationEmailSafe;
|
|
18
16
|
private validateTokenNotExpiredOrUsed;
|
|
@@ -23,19 +21,22 @@ export declare class AuthEmailService {
|
|
|
23
21
|
phone?: string;
|
|
24
22
|
password: string;
|
|
25
23
|
companyData?: PendingRegistration['companyData'];
|
|
26
|
-
additionalFields?: Record<string,
|
|
24
|
+
additionalFields?: Record<string, unknown>;
|
|
27
25
|
verifyUrlBase: string;
|
|
28
26
|
}): Promise<{
|
|
29
27
|
success: boolean;
|
|
30
28
|
message: string;
|
|
29
|
+
messageKey: string;
|
|
31
30
|
}>;
|
|
32
31
|
forgotPassword(email: string, resetUrlBase: string): Promise<{
|
|
33
32
|
success: boolean;
|
|
34
33
|
message: string;
|
|
34
|
+
messageKey: string;
|
|
35
35
|
}>;
|
|
36
36
|
resetPassword(token: string, newPassword: string): Promise<{
|
|
37
37
|
success: boolean;
|
|
38
38
|
message: string;
|
|
39
|
+
messageKey: string;
|
|
39
40
|
}>;
|
|
40
41
|
verifyEmail(token: string): Promise<{
|
|
41
42
|
type: 'pending';
|
|
@@ -43,10 +44,12 @@ export declare class AuthEmailService {
|
|
|
43
44
|
} | {
|
|
44
45
|
type: 'verified';
|
|
45
46
|
message: string;
|
|
47
|
+
messageKey: string;
|
|
46
48
|
}>;
|
|
47
49
|
resendVerificationEmail(email: string, verifyUrlBase: string): Promise<{
|
|
48
50
|
success: boolean;
|
|
49
51
|
message: string;
|
|
52
|
+
messageKey: string;
|
|
50
53
|
}>;
|
|
51
54
|
cleanupExpired(): Promise<{
|
|
52
55
|
deletedTokens: number;
|
|
@@ -20,7 +20,6 @@ export declare class AuthenticationService {
|
|
|
20
20
|
private readonly authEmailService?;
|
|
21
21
|
private readonly userEnricher?;
|
|
22
22
|
private readonly notificationAdapter?;
|
|
23
|
-
private readonly logger;
|
|
24
23
|
constructor(jwtService: JwtService, authConfig: AuthConfigService, cacheManager?: HybridCache, dataSourceProvider?: AuthDataSourceProvider, sessionService?: CompanySelectionSessionService, authEmailService?: AuthEmailService, userEnricher?: IUserEnricher, notificationAdapter?: INotificationAdapter);
|
|
25
24
|
private isCompanyFeatureEnabled;
|
|
26
25
|
private isEmailVerificationEnabled;
|
|
@@ -33,6 +32,7 @@ export declare class AuthenticationService {
|
|
|
33
32
|
register(dto: RegistrationDto, verifyUrlBase?: string): Promise<RegistrationResponse | {
|
|
34
33
|
requiresEmailVerification: true;
|
|
35
34
|
message: string;
|
|
35
|
+
messageKey: string;
|
|
36
36
|
}>;
|
|
37
37
|
private handleExistingUserCompanyRegistration;
|
|
38
38
|
private createUserDirectly;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
|
|
2
|
+
import { FilterAndPaginationDto } from '@flusys/nestjs-shared/dtos';
|
|
2
3
|
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
3
4
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
4
5
|
import { EntityTarget, Repository, SelectQueryBuilder } from 'typeorm';
|
|
@@ -23,7 +24,7 @@ export declare class BranchService extends RequestScopedApiService<CreateCompany
|
|
|
23
24
|
query: SelectQueryBuilder<CompanyBranch>;
|
|
24
25
|
isRaw: boolean;
|
|
25
26
|
}>;
|
|
26
|
-
protected getExtraManipulateQuery(query: SelectQueryBuilder<CompanyBranch>, filterDto:
|
|
27
|
+
protected getExtraManipulateQuery(query: SelectQueryBuilder<CompanyBranch>, filterDto: FilterAndPaginationDto, user: ILoggedUserInfo | null): Promise<{
|
|
27
28
|
query: SelectQueryBuilder<CompanyBranch>;
|
|
28
29
|
isRaw: boolean;
|
|
29
30
|
}>;
|
|
@@ -2,7 +2,7 @@ import { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/clas
|
|
|
2
2
|
import { FilterAndPaginationDto } from '@flusys/nestjs-shared/dtos';
|
|
3
3
|
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
4
4
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
5
|
-
import { EntityTarget, Repository } from 'typeorm';
|
|
5
|
+
import { EntityTarget, Repository, SelectQueryBuilder } from 'typeorm';
|
|
6
6
|
import { AuthConfigService } from './auth-config.service';
|
|
7
7
|
import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
8
8
|
import { CreateUserDto, UpdateUserDto } from '../dtos/user.dto';
|
|
@@ -30,12 +30,12 @@ export declare class UserService extends RequestScopedApiService<CreateUserDto,
|
|
|
30
30
|
private convertRawToResponseDto;
|
|
31
31
|
private convertEntityToDto;
|
|
32
32
|
protected convertEntityListToResponseListDto(entities: User[], isRaw: boolean): IUser[];
|
|
33
|
-
protected getGlobalSearchQuery(query:
|
|
34
|
-
query:
|
|
33
|
+
protected getGlobalSearchQuery(query: SelectQueryBuilder<User>, search: string, _user: ILoggedUserInfo | null): Promise<{
|
|
34
|
+
query: SelectQueryBuilder<User>;
|
|
35
35
|
isRaw: boolean;
|
|
36
36
|
}>;
|
|
37
|
-
protected getExtraManipulateQuery(query:
|
|
38
|
-
query:
|
|
37
|
+
protected getExtraManipulateQuery(query: SelectQueryBuilder<User>, filterAndPaginationDto: FilterAndPaginationDto, user: ILoggedUserInfo | null): Promise<{
|
|
38
|
+
query: SelectQueryBuilder<User>;
|
|
39
39
|
isRaw: boolean;
|
|
40
40
|
}>;
|
|
41
41
|
insert(dto: CreateUserDto, user: ILoggedUserInfo | null): Promise<IUser>;
|
|
@@ -50,9 +50,9 @@ export declare class UserService extends RequestScopedApiService<CreateUserDto,
|
|
|
50
50
|
getEnrichedList(users: IUser[], user: ILoggedUserInfo | null): Promise<IEnrichedUser[]>;
|
|
51
51
|
getProfileSections(): Promise<IProfileSection[]>;
|
|
52
52
|
getProfileSectionData(userId: string, sectionId: string, user: ILoggedUserInfo): Promise<IProfileSectionData | null>;
|
|
53
|
-
updateProfileSection(userId: string, sectionId: string, data: Record<string,
|
|
53
|
+
updateProfileSection(userId: string, sectionId: string, data: Record<string, unknown>, user: ILoggedUserInfo): Promise<void>;
|
|
54
54
|
getProfileCompletion(userId: string, user: ILoggedUserInfo): Promise<number>;
|
|
55
55
|
profile(dto: UpdateUserDto & {
|
|
56
|
-
extras?: Record<string,
|
|
56
|
+
extras?: Record<string, unknown>;
|
|
57
57
|
}, user: ILoggedUserInfo): Promise<string>;
|
|
58
58
|
}
|