@flusys/nestjs-auth 0.1.0-beta.3 → 1.1.0-beta
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 +1 -11
- package/cjs/config/auth.constants.js +14 -4
- package/cjs/controllers/authentication.controller.js +173 -36
- package/cjs/controllers/branch.controller.js +3 -4
- package/cjs/controllers/company.controller.js +3 -4
- package/cjs/controllers/user-permission.controller.js +6 -18
- package/cjs/controllers/user.controller.js +168 -22
- package/cjs/dtos/authentication.dto.js +106 -3
- package/cjs/entities/index.js +5 -1
- package/cjs/entities/pending-registration.entity.js +125 -0
- package/cjs/entities/user-token.entity.js +129 -0
- package/cjs/interfaces/auth-email-provider.interface.js +45 -0
- package/cjs/interfaces/authentication.interface.js +0 -3
- package/cjs/interfaces/index.js +2 -0
- package/cjs/interfaces/user-enricher.interface.js +4 -0
- package/cjs/modules/auth.module.js +8 -2
- package/cjs/services/auth-config.service.js +22 -44
- package/cjs/services/auth-datasource.provider.js +17 -33
- package/cjs/services/auth-email.service.js +452 -0
- package/cjs/services/authentication.service.js +229 -115
- package/cjs/services/branch.service.js +10 -44
- package/cjs/services/company.service.js +7 -36
- package/cjs/services/index.js +1 -0
- package/cjs/services/user-permission.service.js +9 -27
- package/cjs/services/user.service.js +177 -99
- package/cjs/strategies/jwt.strategy.js +14 -1
- package/config/auth.constants.d.ts +3 -0
- package/controllers/authentication.controller.d.ts +13 -9
- package/controllers/user.controller.d.ts +27 -0
- package/dtos/authentication.dto.d.ts +21 -3
- package/dtos/user.dto.d.ts +1 -0
- package/entities/index.d.ts +7 -3
- package/entities/pending-registration.entity.d.ts +17 -0
- package/entities/user-token.entity.d.ts +15 -0
- package/fesm/config/auth.constants.js +18 -8
- package/fesm/controllers/authentication.controller.js +174 -37
- package/fesm/controllers/branch.controller.js +5 -6
- package/fesm/controllers/company.controller.js +5 -6
- package/fesm/controllers/user-permission.controller.js +6 -18
- package/fesm/controllers/user.controller.js +168 -22
- package/fesm/dtos/authentication.dto.js +109 -100
- package/fesm/entities/index.js +7 -1
- package/fesm/entities/pending-registration.entity.js +115 -0
- package/fesm/entities/user-token.entity.js +113 -0
- package/fesm/interfaces/auth-email-provider.interface.js +38 -0
- package/fesm/interfaces/authentication.interface.js +1 -7
- package/fesm/interfaces/index.js +2 -0
- package/fesm/interfaces/user-enricher.interface.js +26 -0
- package/fesm/modules/auth.module.js +9 -3
- package/fesm/services/auth-config.service.js +22 -44
- package/fesm/services/auth-datasource.provider.js +17 -33
- package/fesm/services/auth-email.service.js +401 -0
- package/fesm/services/authentication.service.js +227 -113
- package/fesm/services/branch.service.js +11 -45
- package/fesm/services/company.service.js +8 -37
- package/fesm/services/index.js +1 -0
- package/fesm/services/user-permission.service.js +9 -27
- package/fesm/services/user.service.js +178 -100
- package/fesm/strategies/jwt.strategy.js +14 -1
- package/interfaces/auth-email-provider.interface.d.ts +6 -0
- package/interfaces/auth-module-options.interface.d.ts +4 -1
- package/interfaces/authentication.interface.d.ts +14 -0
- package/interfaces/index.d.ts +2 -0
- package/interfaces/user-enricher.interface.d.ts +67 -0
- package/interfaces/user.interface.d.ts +1 -0
- package/package.json +3 -3
- package/services/auth-config.service.d.ts +1 -0
- package/services/auth-email.service.d.ts +50 -0
- package/services/authentication.service.d.ts +21 -5
- package/services/branch.service.d.ts +2 -5
- package/services/company.service.d.ts +1 -4
- package/services/index.d.ts +1 -0
- package/services/user.service.d.ts +17 -6
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
|
24
|
+
export class PendingRegistration {
|
|
25
|
+
constructor(){
|
|
26
|
+
_define_property(this, "id", void 0);
|
|
27
|
+
_define_property(this, "email", void 0);
|
|
28
|
+
_define_property(this, "name", void 0);
|
|
29
|
+
_define_property(this, "phone", void 0);
|
|
30
|
+
_define_property(this, "passwordHash", void 0);
|
|
31
|
+
_define_property(this, "token", void 0);
|
|
32
|
+
_define_property(this, "expiresAt", void 0);
|
|
33
|
+
_define_property(this, "companyData", void 0);
|
|
34
|
+
_define_property(this, "createdAt", void 0);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
_ts_decorate([
|
|
38
|
+
PrimaryGeneratedColumn('uuid'),
|
|
39
|
+
_ts_metadata("design:type", String)
|
|
40
|
+
], PendingRegistration.prototype, "id", void 0);
|
|
41
|
+
_ts_decorate([
|
|
42
|
+
Column({
|
|
43
|
+
type: 'varchar',
|
|
44
|
+
length: 255
|
|
45
|
+
}),
|
|
46
|
+
_ts_metadata("design:type", String)
|
|
47
|
+
], PendingRegistration.prototype, "email", void 0);
|
|
48
|
+
_ts_decorate([
|
|
49
|
+
Column({
|
|
50
|
+
type: 'varchar',
|
|
51
|
+
length: 255,
|
|
52
|
+
nullable: true
|
|
53
|
+
}),
|
|
54
|
+
_ts_metadata("design:type", Object)
|
|
55
|
+
], PendingRegistration.prototype, "name", void 0);
|
|
56
|
+
_ts_decorate([
|
|
57
|
+
Column({
|
|
58
|
+
type: 'varchar',
|
|
59
|
+
length: 255,
|
|
60
|
+
nullable: true
|
|
61
|
+
}),
|
|
62
|
+
_ts_metadata("design:type", Object)
|
|
63
|
+
], PendingRegistration.prototype, "phone", void 0);
|
|
64
|
+
_ts_decorate([
|
|
65
|
+
Column({
|
|
66
|
+
type: 'text',
|
|
67
|
+
name: 'passwordHash'
|
|
68
|
+
}),
|
|
69
|
+
_ts_metadata("design:type", String)
|
|
70
|
+
], PendingRegistration.prototype, "passwordHash", void 0);
|
|
71
|
+
_ts_decorate([
|
|
72
|
+
Column({
|
|
73
|
+
type: 'varchar',
|
|
74
|
+
length: 64
|
|
75
|
+
}),
|
|
76
|
+
_ts_metadata("design:type", String)
|
|
77
|
+
], PendingRegistration.prototype, "token", void 0);
|
|
78
|
+
_ts_decorate([
|
|
79
|
+
Column({
|
|
80
|
+
type: 'timestamp',
|
|
81
|
+
name: 'expires_at'
|
|
82
|
+
}),
|
|
83
|
+
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
84
|
+
], PendingRegistration.prototype, "expiresAt", void 0);
|
|
85
|
+
_ts_decorate([
|
|
86
|
+
Column({
|
|
87
|
+
type: 'json',
|
|
88
|
+
nullable: true,
|
|
89
|
+
name: 'company_data'
|
|
90
|
+
}),
|
|
91
|
+
_ts_metadata("design:type", Object)
|
|
92
|
+
], PendingRegistration.prototype, "companyData", void 0);
|
|
93
|
+
_ts_decorate([
|
|
94
|
+
CreateDateColumn({
|
|
95
|
+
type: 'timestamp',
|
|
96
|
+
name: 'created_at'
|
|
97
|
+
}),
|
|
98
|
+
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
99
|
+
], PendingRegistration.prototype, "createdAt", void 0);
|
|
100
|
+
PendingRegistration = _ts_decorate([
|
|
101
|
+
Entity('pending_registrations'),
|
|
102
|
+
Index('IDX_pending_reg_email', [
|
|
103
|
+
'email'
|
|
104
|
+
], {
|
|
105
|
+
unique: true
|
|
106
|
+
}),
|
|
107
|
+
Index('IDX_pending_reg_token', [
|
|
108
|
+
'token'
|
|
109
|
+
], {
|
|
110
|
+
unique: true
|
|
111
|
+
}),
|
|
112
|
+
Index('IDX_pending_reg_expires', [
|
|
113
|
+
'expiresAt'
|
|
114
|
+
])
|
|
115
|
+
], PendingRegistration);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
|
24
|
+
/**
|
|
25
|
+
* Token types for various user actions
|
|
26
|
+
*/ export var TokenType = /*#__PURE__*/ function(TokenType) {
|
|
27
|
+
TokenType["PASSWORD_RESET"] = "password_reset";
|
|
28
|
+
TokenType["EMAIL_VERIFICATION"] = "email_verification";
|
|
29
|
+
TokenType["EMAIL_CHANGE"] = "email_change";
|
|
30
|
+
return TokenType;
|
|
31
|
+
}({});
|
|
32
|
+
export class UserToken {
|
|
33
|
+
constructor(){
|
|
34
|
+
_define_property(this, "id", void 0);
|
|
35
|
+
_define_property(this, "userId", void 0);
|
|
36
|
+
_define_property(this, "type", void 0);
|
|
37
|
+
_define_property(this, "token", void 0);
|
|
38
|
+
_define_property(this, "expiresAt", void 0);
|
|
39
|
+
_define_property(this, "usedAt", void 0);
|
|
40
|
+
_define_property(this, "metadata", void 0);
|
|
41
|
+
_define_property(this, "createdAt", void 0);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
_ts_decorate([
|
|
45
|
+
PrimaryGeneratedColumn('uuid'),
|
|
46
|
+
_ts_metadata("design:type", String)
|
|
47
|
+
], UserToken.prototype, "id", void 0);
|
|
48
|
+
_ts_decorate([
|
|
49
|
+
Column({
|
|
50
|
+
type: 'uuid',
|
|
51
|
+
name: 'user_id'
|
|
52
|
+
}),
|
|
53
|
+
_ts_metadata("design:type", String)
|
|
54
|
+
], UserToken.prototype, "userId", void 0);
|
|
55
|
+
_ts_decorate([
|
|
56
|
+
Column({
|
|
57
|
+
type: 'varchar',
|
|
58
|
+
length: 32
|
|
59
|
+
}),
|
|
60
|
+
_ts_metadata("design:type", String)
|
|
61
|
+
], UserToken.prototype, "type", void 0);
|
|
62
|
+
_ts_decorate([
|
|
63
|
+
Column({
|
|
64
|
+
type: 'varchar',
|
|
65
|
+
length: 64
|
|
66
|
+
}),
|
|
67
|
+
_ts_metadata("design:type", String)
|
|
68
|
+
], UserToken.prototype, "token", void 0);
|
|
69
|
+
_ts_decorate([
|
|
70
|
+
Column({
|
|
71
|
+
type: 'timestamp',
|
|
72
|
+
name: 'expires_at'
|
|
73
|
+
}),
|
|
74
|
+
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
75
|
+
], UserToken.prototype, "expiresAt", void 0);
|
|
76
|
+
_ts_decorate([
|
|
77
|
+
Column({
|
|
78
|
+
type: 'timestamp',
|
|
79
|
+
name: 'used_at',
|
|
80
|
+
nullable: true
|
|
81
|
+
}),
|
|
82
|
+
_ts_metadata("design:type", Object)
|
|
83
|
+
], UserToken.prototype, "usedAt", void 0);
|
|
84
|
+
_ts_decorate([
|
|
85
|
+
Column({
|
|
86
|
+
type: 'varchar',
|
|
87
|
+
length: 255,
|
|
88
|
+
nullable: true
|
|
89
|
+
}),
|
|
90
|
+
_ts_metadata("design:type", Object)
|
|
91
|
+
], UserToken.prototype, "metadata", void 0);
|
|
92
|
+
_ts_decorate([
|
|
93
|
+
CreateDateColumn({
|
|
94
|
+
type: 'timestamp',
|
|
95
|
+
name: 'created_at'
|
|
96
|
+
}),
|
|
97
|
+
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
98
|
+
], UserToken.prototype, "createdAt", void 0);
|
|
99
|
+
UserToken = _ts_decorate([
|
|
100
|
+
Entity('user_tokens'),
|
|
101
|
+
Index('IDX_user_token_token', [
|
|
102
|
+
'token'
|
|
103
|
+
], {
|
|
104
|
+
unique: true
|
|
105
|
+
}),
|
|
106
|
+
Index('IDX_user_token_user_type', [
|
|
107
|
+
'userId',
|
|
108
|
+
'type'
|
|
109
|
+
]),
|
|
110
|
+
Index('IDX_user_token_expires', [
|
|
111
|
+
'expiresAt'
|
|
112
|
+
])
|
|
113
|
+
], UserToken);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Email Provider Interface
|
|
3
|
+
*
|
|
4
|
+
* This interface defines the contract for email sending in the auth module.
|
|
5
|
+
* It allows auth to work independently of the email module.
|
|
6
|
+
*
|
|
7
|
+
* To use email features in auth:
|
|
8
|
+
* 1. Provide AUTH_EMAIL_PROVIDER in your app module
|
|
9
|
+
* 2. Implement IAuthEmailProvider using EmailSendService from nestjs-email
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* // In app.module.ts or a provider file
|
|
14
|
+
* {
|
|
15
|
+
* provide: AUTH_EMAIL_PROVIDER,
|
|
16
|
+
* useFactory: (emailSendService: EmailSendService) => ({
|
|
17
|
+
* sendPasswordResetEmail: async (email, token, resetUrl) => {
|
|
18
|
+
* await emailSendService.sendTemplateEmail({
|
|
19
|
+
* templateSlug: 'password-reset',
|
|
20
|
+
* to: email,
|
|
21
|
+
* variables: { resetUrl, token },
|
|
22
|
+
* });
|
|
23
|
+
* },
|
|
24
|
+
* sendVerificationEmail: async (email, token, verifyUrl) => {
|
|
25
|
+
* await emailSendService.sendTemplateEmail({
|
|
26
|
+
* templateSlug: 'email-verification',
|
|
27
|
+
* to: email,
|
|
28
|
+
* variables: { verifyUrl, token },
|
|
29
|
+
* });
|
|
30
|
+
* },
|
|
31
|
+
* }),
|
|
32
|
+
* inject: [EmailSendService],
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/ /**
|
|
36
|
+
* Injection token for auth email provider
|
|
37
|
+
* Use this to provide the email implementation
|
|
38
|
+
*/ export const AUTH_EMAIL_PROVIDER = 'AUTH_EMAIL_PROVIDER';
|
|
@@ -1,7 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// TOKEN INTERFACES
|
|
3
|
-
// =============================================================================
|
|
4
|
-
/**
|
|
5
|
-
* Login Response when company/branch selection is required
|
|
6
|
-
* Frontend shows selection UI and calls /select endpoint with chosen company + branch
|
|
7
|
-
*/ export { };
|
|
1
|
+
/** Login Response when email verification is required */ export { };
|
package/fesm/interfaces/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export * from './authentication.interface';
|
|
2
|
+
export * from './auth-email-provider.interface';
|
|
2
3
|
export * from './company-branch.interface';
|
|
3
4
|
export * from './company.interface';
|
|
4
5
|
export * from './user.interface';
|
|
6
|
+
export * from './user-enricher.interface';
|
|
5
7
|
export * from './auth-module-options.interface';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Enricher Provider Interface
|
|
3
|
+
*
|
|
4
|
+
* Allows consuming applications to extend user list and profile
|
|
5
|
+
* without modifying the base nestjs-auth package.
|
|
6
|
+
*
|
|
7
|
+
* This follows the Provider Interface Pattern:
|
|
8
|
+
* - nestjs-auth defines the interface (this file)
|
|
9
|
+
* - Consuming apps implement the interface
|
|
10
|
+
* - nestjs-auth optionally injects the implementation
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // In your app module
|
|
14
|
+
* providers: [
|
|
15
|
+
* { provide: USER_ENRICHER, useClass: MyUserEnricher },
|
|
16
|
+
* ]
|
|
17
|
+
*
|
|
18
|
+
* // Your enricher implementation
|
|
19
|
+
* @Injectable()
|
|
20
|
+
* export class MyUserEnricher implements IUserEnricher {
|
|
21
|
+
* async enrichListQuery(query, user) {
|
|
22
|
+
* query.leftJoinAndSelect('user.department', 'department');
|
|
23
|
+
* return { extraFields: ['department.name'] };
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
*/ export { };
|
|
@@ -17,14 +17,14 @@ import { Company, CompanyBranch, User, UserCompanyPermission } from '../entities
|
|
|
17
17
|
import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
|
|
18
18
|
import { ClearToken } from '../interceptors/clear-token.interceptor';
|
|
19
19
|
import { SetToken } from '../interceptors/set-token.interceptor';
|
|
20
|
-
import { AuthenticationService, BranchService, CompanySelectionSessionService, CompanyService, UserPermissionService, UserService } from '../services';
|
|
20
|
+
import { AuthEmailService, AuthenticationService, BranchService, CompanySelectionSessionService, CompanyService, UserPermissionService, UserService } from '../services';
|
|
21
21
|
import { JwtStrategy } from '../strategies/jwt.strategy';
|
|
22
22
|
export class AuthModule {
|
|
23
23
|
static forRoot(options) {
|
|
24
24
|
return this.buildDynamicModule(options);
|
|
25
25
|
}
|
|
26
26
|
static forRootAsync(asyncOptions) {
|
|
27
|
-
const { global = false, includeController = true, bootstrapAppConfig: { enableCompanyFeature } = {}, imports: externalImports = [] } = asyncOptions;
|
|
27
|
+
const { global = false, includeController = true, bootstrapAppConfig: { enableCompanyFeature } = {}, imports: externalImports = [], providers: additionalProviders = [] } = asyncOptions;
|
|
28
28
|
const asyncProviders = this.createAsyncProviders(asyncOptions);
|
|
29
29
|
// Build imports
|
|
30
30
|
const imports = [
|
|
@@ -51,6 +51,7 @@ export class AuthModule {
|
|
|
51
51
|
// Build providers
|
|
52
52
|
const providers = [
|
|
53
53
|
...asyncProviders,
|
|
54
|
+
...additionalProviders,
|
|
54
55
|
// Config & Infrastructure
|
|
55
56
|
AuthConfigService,
|
|
56
57
|
AuthDataSourceProvider,
|
|
@@ -58,6 +59,7 @@ export class AuthModule {
|
|
|
58
59
|
JwtStrategy,
|
|
59
60
|
JwtAuthGuard,
|
|
60
61
|
AuthenticationService,
|
|
62
|
+
AuthEmailService,
|
|
61
63
|
// Interceptors
|
|
62
64
|
SetToken,
|
|
63
65
|
ClearToken,
|
|
@@ -77,6 +79,7 @@ export class AuthModule {
|
|
|
77
79
|
JwtStrategy,
|
|
78
80
|
JwtAuthGuard,
|
|
79
81
|
AuthenticationService,
|
|
82
|
+
AuthEmailService,
|
|
80
83
|
UserService
|
|
81
84
|
];
|
|
82
85
|
if (enableCompanyFeature) {
|
|
@@ -92,7 +95,7 @@ export class AuthModule {
|
|
|
92
95
|
};
|
|
93
96
|
}
|
|
94
97
|
static buildDynamicModule(options) {
|
|
95
|
-
const { bootstrapAppConfig: { enableCompanyFeature } = {}, includeController = true, global = false } = options;
|
|
98
|
+
const { bootstrapAppConfig: { enableCompanyFeature } = {}, includeController = true, global = false, providers: additionalProviders = [] } = options;
|
|
96
99
|
const imports = [
|
|
97
100
|
PassportModule.register({
|
|
98
101
|
defaultStrategy: 'jwt'
|
|
@@ -118,6 +121,7 @@ export class AuthModule {
|
|
|
118
121
|
provide: AUTH_MODULE_OPTIONS,
|
|
119
122
|
useValue: options
|
|
120
123
|
},
|
|
124
|
+
...additionalProviders,
|
|
121
125
|
// Config & Infrastructure
|
|
122
126
|
AuthConfigService,
|
|
123
127
|
AuthDataSourceProvider,
|
|
@@ -125,6 +129,7 @@ export class AuthModule {
|
|
|
125
129
|
JwtStrategy,
|
|
126
130
|
JwtAuthGuard,
|
|
127
131
|
AuthenticationService,
|
|
132
|
+
AuthEmailService,
|
|
128
133
|
// Interceptors
|
|
129
134
|
SetToken,
|
|
130
135
|
ClearToken,
|
|
@@ -143,6 +148,7 @@ export class AuthModule {
|
|
|
143
148
|
JwtStrategy,
|
|
144
149
|
JwtAuthGuard,
|
|
145
150
|
AuthenticationService,
|
|
151
|
+
AuthEmailService,
|
|
146
152
|
UserService
|
|
147
153
|
];
|
|
148
154
|
if (enableCompanyFeature) {
|
|
@@ -29,76 +29,54 @@ import { Inject, Injectable, Optional } from '@nestjs/common';
|
|
|
29
29
|
import { AuthModuleOptions } from '../interfaces/auth-module-options.interface';
|
|
30
30
|
import { AUTH_MODULE_OPTIONS } from '../config/auth.constants';
|
|
31
31
|
export class AuthConfigService {
|
|
32
|
-
//
|
|
33
|
-
/**
|
|
34
|
-
* Get database mode (single or multi-tenant)
|
|
35
|
-
*/ getDatabaseMode() {
|
|
32
|
+
// Database Mode
|
|
33
|
+
/** Get database mode (single or multi-tenant) */ getDatabaseMode() {
|
|
36
34
|
return this.options.bootstrapAppConfig?.databaseMode ?? 'single';
|
|
37
35
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Check if running in multi-tenant mode
|
|
40
|
-
*/ isMultiTenant() {
|
|
36
|
+
/** Check if running in multi-tenant mode */ isMultiTenant() {
|
|
41
37
|
return this.getDatabaseMode() === 'multi-tenant';
|
|
42
38
|
}
|
|
43
|
-
//
|
|
44
|
-
/**
|
|
45
|
-
* Check if company feature is enabled
|
|
46
|
-
*/ isCompanyFeatureEnabled() {
|
|
39
|
+
// Feature Flags
|
|
40
|
+
/** Check if company feature is enabled */ isCompanyFeatureEnabled() {
|
|
47
41
|
return this.options.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
48
42
|
}
|
|
49
|
-
//
|
|
50
|
-
/**
|
|
51
|
-
* Get JWT secret key
|
|
52
|
-
*/ getJwtSecret() {
|
|
43
|
+
// JWT Configuration
|
|
44
|
+
/** Get JWT secret key */ getJwtSecret() {
|
|
53
45
|
return this.config.jwtSecret ?? 'default-secret-change-in-production';
|
|
54
46
|
}
|
|
55
|
-
/**
|
|
56
|
-
* Get JWT expiration time
|
|
57
|
-
*/ getJwtExpiration() {
|
|
47
|
+
/** Get JWT expiration time */ getJwtExpiration() {
|
|
58
48
|
return this.config.jwtExpiration ?? '1h';
|
|
59
49
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Get refresh token secret key
|
|
62
|
-
*/ getRefreshTokenSecret() {
|
|
50
|
+
/** Get refresh token secret key */ getRefreshTokenSecret() {
|
|
63
51
|
return this.config.refreshTokenSecret ?? this.getJwtSecret();
|
|
64
52
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Get refresh token expiration time
|
|
67
|
-
*/ getRefreshTokenExpiration() {
|
|
53
|
+
/** Get refresh token expiration time */ getRefreshTokenExpiration() {
|
|
68
54
|
return this.config.refreshTokenExpiration ?? '7d';
|
|
69
55
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Get refresh token cookie name
|
|
72
|
-
*/ getRefreshTokenCookieName() {
|
|
56
|
+
/** Get refresh token cookie name */ getRefreshTokenCookieName() {
|
|
73
57
|
return this.config.refreshTokenCookieName ?? 'fsn_refresh_token';
|
|
74
58
|
}
|
|
75
|
-
//
|
|
76
|
-
/**
|
|
77
|
-
|
|
78
|
-
|
|
59
|
+
// Frontend Configuration
|
|
60
|
+
/** Get frontend URL for email links */ getFrontendUrl() {
|
|
61
|
+
return this.config.frontendUrl;
|
|
62
|
+
}
|
|
63
|
+
// Database Configuration
|
|
64
|
+
/** Get default database config */ getDefaultDatabaseConfig() {
|
|
79
65
|
return this.config.defaultDatabaseConfig ?? this.config.tenantDefaultDatabaseConfig;
|
|
80
66
|
}
|
|
81
|
-
/**
|
|
82
|
-
* Get tenant default database config (for multi-tenant)
|
|
83
|
-
*/ getTenantDefaultDatabaseConfig() {
|
|
67
|
+
/** Get tenant default database config (for multi-tenant) */ getTenantDefaultDatabaseConfig() {
|
|
84
68
|
return this.config.tenantDefaultDatabaseConfig;
|
|
85
69
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Get registered tenants
|
|
88
|
-
*/ getTenants() {
|
|
70
|
+
/** Get registered tenants */ getTenants() {
|
|
89
71
|
return this.config.tenants ?? [];
|
|
90
72
|
}
|
|
91
|
-
//
|
|
92
|
-
/**
|
|
93
|
-
* Get full module options
|
|
94
|
-
*/ getOptions() {
|
|
73
|
+
// Raw Options
|
|
74
|
+
/** Get full module options */ getOptions() {
|
|
95
75
|
return {
|
|
96
76
|
...this.options
|
|
97
77
|
};
|
|
98
78
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Get runtime config
|
|
101
|
-
*/ getConfig() {
|
|
79
|
+
/** Get runtime config */ getConfig() {
|
|
102
80
|
return {
|
|
103
81
|
...this.config
|
|
104
82
|
};
|
|
@@ -32,11 +32,8 @@ import { Request } from 'express';
|
|
|
32
32
|
import { AuthModuleOptions } from '../interfaces/auth-module-options.interface';
|
|
33
33
|
import { AUTH_MODULE_OPTIONS } from '../config/auth.constants';
|
|
34
34
|
export class AuthDataSourceProvider extends MultiTenantDataSourceService {
|
|
35
|
-
//
|
|
36
|
-
/**
|
|
37
|
-
* CRITICAL: Override to access Auth-specific static properties
|
|
38
|
-
* Without this, parent class methods would access parent's static properties
|
|
39
|
-
*/ async getSingleDataSource() {
|
|
35
|
+
// Override Static Cache Access Methods
|
|
36
|
+
/** Override to access Auth-specific static properties */ async getSingleDataSource() {
|
|
40
37
|
if (!AuthDataSourceProvider.singleDataSource) {
|
|
41
38
|
if (AuthDataSourceProvider.singleConnectionLock) {
|
|
42
39
|
return AuthDataSourceProvider.singleConnectionLock;
|
|
@@ -60,9 +57,7 @@ export class AuthDataSourceProvider extends MultiTenantDataSourceService {
|
|
|
60
57
|
}
|
|
61
58
|
return AuthDataSourceProvider.singleDataSource;
|
|
62
59
|
}
|
|
63
|
-
/**
|
|
64
|
-
* CRITICAL: Override to use Auth-specific static cache for tenant connections
|
|
65
|
-
*/ async getOrCreateTenantConnection(tenant) {
|
|
60
|
+
/** Override to use Auth-specific static cache for tenant connections */ async getOrCreateTenantConnection(tenant) {
|
|
66
61
|
// Return existing initialized connection from Auth-specific cache
|
|
67
62
|
const existing = AuthDataSourceProvider.tenantConnections.get(tenant.id);
|
|
68
63
|
if (existing?.isInitialized) {
|
|
@@ -85,10 +80,8 @@ export class AuthDataSourceProvider extends MultiTenantDataSourceService {
|
|
|
85
80
|
AuthDataSourceProvider.connectionLocks.delete(tenant.id);
|
|
86
81
|
}
|
|
87
82
|
}
|
|
88
|
-
//
|
|
89
|
-
/**
|
|
90
|
-
* Build parent options from AuthModuleOptions
|
|
91
|
-
*/ static buildParentOptions(options) {
|
|
83
|
+
// Factory Methods
|
|
84
|
+
/** Build parent options from AuthModuleOptions */ static buildParentOptions(options) {
|
|
92
85
|
return {
|
|
93
86
|
bootstrapAppConfig: options.bootstrapAppConfig,
|
|
94
87
|
defaultDatabaseConfig: options.config?.defaultDatabaseConfig,
|
|
@@ -96,31 +89,24 @@ export class AuthDataSourceProvider extends MultiTenantDataSourceService {
|
|
|
96
89
|
tenants: options.config?.tenants
|
|
97
90
|
};
|
|
98
91
|
}
|
|
99
|
-
//
|
|
100
|
-
/**
|
|
101
|
-
* Get global enable company feature flag
|
|
102
|
-
*/ getEnableCompanyFeature() {
|
|
92
|
+
// Feature Flags
|
|
93
|
+
/** Get global enable company feature flag */ getEnableCompanyFeature() {
|
|
103
94
|
return this.authOptions.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
104
95
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Get enable company feature for specific tenant
|
|
107
|
-
* Falls back to global setting if not specified per-tenant
|
|
108
|
-
*/ getEnableCompanyFeatureForTenant(tenant) {
|
|
96
|
+
/** Get enable company feature for specific tenant. Falls back to global setting. */ getEnableCompanyFeatureForTenant(tenant) {
|
|
109
97
|
return tenant?.enableCompanyFeature ?? this.getEnableCompanyFeature();
|
|
110
98
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Get enable company feature for current request context
|
|
113
|
-
*/ getEnableCompanyFeatureForCurrentTenant() {
|
|
99
|
+
/** Get enable company feature for current request context */ getEnableCompanyFeatureForCurrentTenant() {
|
|
114
100
|
return this.getEnableCompanyFeatureForTenant(this.getCurrentTenant() ?? undefined);
|
|
115
101
|
}
|
|
116
|
-
//
|
|
117
|
-
/**
|
|
118
|
-
* Get auth entities based on company feature flag
|
|
119
|
-
*/ async getAuthEntities(enableCompanyFeature) {
|
|
102
|
+
// Entity Management
|
|
103
|
+
/** Get auth entities based on company feature flag */ async getAuthEntities(enableCompanyFeature) {
|
|
120
104
|
const enable = enableCompanyFeature ?? this.getEnableCompanyFeature();
|
|
121
|
-
const { Company, CompanyBranch, User, UserCompanyPermission } = await import('../entities');
|
|
105
|
+
const { Company, CompanyBranch, User, UserCompanyPermission, UserToken, PendingRegistration } = await import('../entities');
|
|
122
106
|
const coreEntities = [
|
|
123
|
-
User
|
|
107
|
+
User,
|
|
108
|
+
UserToken,
|
|
109
|
+
PendingRegistration
|
|
124
110
|
];
|
|
125
111
|
const companyEntities = enable ? [
|
|
126
112
|
Company,
|
|
@@ -132,10 +118,8 @@ export class AuthDataSourceProvider extends MultiTenantDataSourceService {
|
|
|
132
118
|
...companyEntities
|
|
133
119
|
];
|
|
134
120
|
}
|
|
135
|
-
//
|
|
136
|
-
/**
|
|
137
|
-
* Override to dynamically set entities based on tenant config
|
|
138
|
-
*/ async createDataSourceFromConfig(config) {
|
|
121
|
+
// Overrides
|
|
122
|
+
/** Override to dynamically set entities based on tenant config */ async createDataSourceFromConfig(config) {
|
|
139
123
|
const currentTenant = this.getCurrentTenant();
|
|
140
124
|
const enableCompanyFeature = this.getEnableCompanyFeatureForTenant(currentTenant ?? undefined);
|
|
141
125
|
const entities = await this.getAuthEntities(enableCompanyFeature);
|