@flusys/nestjs-auth 1.0.0-beta → 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/cjs/config/auth.constants.js +4 -0
- package/cjs/controllers/authentication.controller.js +167 -5
- package/cjs/controllers/user.controller.js +151 -0
- 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/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 +4 -0
- package/cjs/services/auth-datasource.provider.js +4 -2
- package/cjs/services/auth-email.service.js +452 -0
- package/cjs/services/authentication.service.js +156 -37
- package/cjs/services/index.js +1 -0
- package/cjs/services/user.service.js +147 -11
- package/config/auth.constants.d.ts +1 -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 +11 -0
- package/fesm/controllers/authentication.controller.js +168 -6
- package/fesm/controllers/user.controller.js +151 -0
- package/fesm/dtos/authentication.dto.js +92 -3
- 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 -1
- 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 +4 -0
- package/fesm/services/auth-datasource.provider.js +4 -2
- package/fesm/services/auth-email.service.js +401 -0
- package/fesm/services/authentication.service.js +156 -37
- package/fesm/services/index.js +1 -0
- package/fesm/services/user.service.js +149 -13
- 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 +16 -4
- package/services/index.d.ts +1 -0
- package/services/user.service.d.ts +16 -3
|
@@ -503,16 +503,17 @@ _ts_decorate([
|
|
|
503
503
|
_define_property(this, "company", void 0);
|
|
504
504
|
_define_property(this, "branch", void 0);
|
|
505
505
|
_define_property(this, "companyFeatureEnabled", void 0);
|
|
506
|
+
_define_property(this, "requiresEmailVerification", void 0);
|
|
506
507
|
}
|
|
507
508
|
}
|
|
508
509
|
_ts_decorate([
|
|
509
|
-
|
|
510
|
+
ApiPropertyOptional({
|
|
510
511
|
example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
|
|
511
512
|
}),
|
|
512
513
|
_ts_metadata("design:type", String)
|
|
513
514
|
], RegistrationResponseDto.prototype, "accessToken", void 0);
|
|
514
515
|
_ts_decorate([
|
|
515
|
-
|
|
516
|
+
ApiPropertyOptional({
|
|
516
517
|
example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
|
|
517
518
|
}),
|
|
518
519
|
_ts_metadata("design:type", String)
|
|
@@ -539,12 +540,19 @@ _ts_decorate([
|
|
|
539
540
|
_ts_metadata("design:type", typeof BranchInfoDto === "undefined" ? Object : BranchInfoDto)
|
|
540
541
|
], RegistrationResponseDto.prototype, "branch", void 0);
|
|
541
542
|
_ts_decorate([
|
|
542
|
-
|
|
543
|
+
ApiPropertyOptional({
|
|
543
544
|
example: true,
|
|
544
545
|
description: 'Whether company feature is enabled'
|
|
545
546
|
}),
|
|
546
547
|
_ts_metadata("design:type", Boolean)
|
|
547
548
|
], RegistrationResponseDto.prototype, "companyFeatureEnabled", void 0);
|
|
549
|
+
_ts_decorate([
|
|
550
|
+
ApiPropertyOptional({
|
|
551
|
+
example: true,
|
|
552
|
+
description: 'Whether email verification is required'
|
|
553
|
+
}),
|
|
554
|
+
_ts_metadata("design:type", Boolean)
|
|
555
|
+
], RegistrationResponseDto.prototype, "requiresEmailVerification", void 0);
|
|
548
556
|
/** Me Response DTO - Returned by GET /auth/me (user data only, no tokens) */ export class MeResponseDto {
|
|
549
557
|
constructor(){
|
|
550
558
|
_define_property(this, "user", void 0);
|
|
@@ -619,3 +627,84 @@ _ts_decorate([
|
|
|
619
627
|
}),
|
|
620
628
|
_ts_metadata("design:type", Array)
|
|
621
629
|
], CompanySelectionResponseDto.prototype, "companies", void 0);
|
|
630
|
+
/** Login Response when email verification required */ export class LoginResponseRequiresEmailVerificationDto {
|
|
631
|
+
constructor(){
|
|
632
|
+
_define_property(this, "requiresEmailVerification", void 0);
|
|
633
|
+
_define_property(this, "email", void 0);
|
|
634
|
+
_define_property(this, "message", void 0);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
_ts_decorate([
|
|
638
|
+
ApiProperty({
|
|
639
|
+
example: true
|
|
640
|
+
}),
|
|
641
|
+
_ts_metadata("design:type", Boolean)
|
|
642
|
+
], LoginResponseRequiresEmailVerificationDto.prototype, "requiresEmailVerification", void 0);
|
|
643
|
+
_ts_decorate([
|
|
644
|
+
ApiProperty({
|
|
645
|
+
example: 'john@example.com'
|
|
646
|
+
}),
|
|
647
|
+
_ts_metadata("design:type", String)
|
|
648
|
+
], LoginResponseRequiresEmailVerificationDto.prototype, "email", void 0);
|
|
649
|
+
_ts_decorate([
|
|
650
|
+
ApiProperty({
|
|
651
|
+
example: 'Please verify your email address before logging in.'
|
|
652
|
+
}),
|
|
653
|
+
_ts_metadata("design:type", String)
|
|
654
|
+
], LoginResponseRequiresEmailVerificationDto.prototype, "message", void 0);
|
|
655
|
+
// Email Auth DTOs
|
|
656
|
+
/** Forgot Password Request DTO - Initiates password reset flow */ export class ForgotPasswordDto {
|
|
657
|
+
constructor(){
|
|
658
|
+
_define_property(this, "email", void 0);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
_ts_decorate([
|
|
662
|
+
ApiProperty({
|
|
663
|
+
example: 'john@example.com',
|
|
664
|
+
description: 'Email address to send reset link to'
|
|
665
|
+
}),
|
|
666
|
+
IsEmail(),
|
|
667
|
+
IsNotEmpty(),
|
|
668
|
+
_ts_metadata("design:type", String)
|
|
669
|
+
], ForgotPasswordDto.prototype, "email", void 0);
|
|
670
|
+
/** Reset Password Request DTO - Resets password using token */ export class ResetPasswordDto {
|
|
671
|
+
constructor(){
|
|
672
|
+
_define_property(this, "token", void 0);
|
|
673
|
+
_define_property(this, "newPassword", void 0);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
_ts_decorate([
|
|
677
|
+
ApiProperty({
|
|
678
|
+
example: 'a1b2c3d4e5f6...',
|
|
679
|
+
description: 'Password reset token from email'
|
|
680
|
+
}),
|
|
681
|
+
IsString(),
|
|
682
|
+
IsNotEmpty(),
|
|
683
|
+
_ts_metadata("design:type", String)
|
|
684
|
+
], ResetPasswordDto.prototype, "token", void 0);
|
|
685
|
+
_ts_decorate([
|
|
686
|
+
ApiProperty({
|
|
687
|
+
example: 'newSecurePassword123',
|
|
688
|
+
description: 'New password (min 6 chars)'
|
|
689
|
+
}),
|
|
690
|
+
IsString(),
|
|
691
|
+
IsNotEmpty(),
|
|
692
|
+
MinLength(6),
|
|
693
|
+
_ts_metadata("design:type", String)
|
|
694
|
+
], ResetPasswordDto.prototype, "newPassword", void 0);
|
|
695
|
+
/** Verify Email Request DTO - Verifies email using token */ export class VerifyEmailDto {
|
|
696
|
+
constructor(){
|
|
697
|
+
_define_property(this, "token", void 0);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
_ts_decorate([
|
|
701
|
+
ApiProperty({
|
|
702
|
+
example: 'a1b2c3d4e5f6...',
|
|
703
|
+
description: 'Email verification token from email'
|
|
704
|
+
}),
|
|
705
|
+
IsString(),
|
|
706
|
+
IsNotEmpty(),
|
|
707
|
+
_ts_metadata("design:type", String)
|
|
708
|
+
], VerifyEmailDto.prototype, "token", void 0);
|
|
709
|
+
/** Resend Verification Email Request DTO - Same structure as ForgotPasswordDto */ export class ResendVerificationDto extends ForgotPasswordDto {
|
|
710
|
+
}
|
package/fesm/entities/index.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
export * from './company-branch.entity';
|
|
2
2
|
export * from './company.entity';
|
|
3
|
+
export * from './pending-registration.entity';
|
|
3
4
|
export { PermissionType, UserCompanyPermission } from './user-company-permission.entity';
|
|
4
5
|
export * from './user.entity';
|
|
6
|
+
export * from './user-token.entity';
|
|
5
7
|
import { CompanyBranch } from './company-branch.entity';
|
|
6
8
|
import { Company } from './company.entity';
|
|
9
|
+
import { PendingRegistration } from './pending-registration.entity';
|
|
7
10
|
import { UserCompanyPermission } from './user-company-permission.entity';
|
|
8
11
|
import { User } from './user.entity';
|
|
12
|
+
import { UserToken } from './user-token.entity';
|
|
9
13
|
export const CoreEntities = [
|
|
10
|
-
User
|
|
14
|
+
User,
|
|
15
|
+
UserToken,
|
|
16
|
+
PendingRegistration
|
|
11
17
|
];
|
|
12
18
|
export const CompanyEntities = [
|
|
13
19
|
Company,
|
|
@@ -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 +1 @@
|
|
|
1
|
-
/** Login Response when
|
|
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) {
|
|
@@ -56,6 +56,10 @@ export class AuthConfigService {
|
|
|
56
56
|
/** Get refresh token cookie name */ getRefreshTokenCookieName() {
|
|
57
57
|
return this.config.refreshTokenCookieName ?? 'fsn_refresh_token';
|
|
58
58
|
}
|
|
59
|
+
// Frontend Configuration
|
|
60
|
+
/** Get frontend URL for email links */ getFrontendUrl() {
|
|
61
|
+
return this.config.frontendUrl;
|
|
62
|
+
}
|
|
59
63
|
// Database Configuration
|
|
60
64
|
/** Get default database config */ getDefaultDatabaseConfig() {
|
|
61
65
|
return this.config.defaultDatabaseConfig ?? this.config.tenantDefaultDatabaseConfig;
|
|
@@ -102,9 +102,11 @@ export class AuthDataSourceProvider extends MultiTenantDataSourceService {
|
|
|
102
102
|
// Entity Management
|
|
103
103
|
/** Get auth entities based on company feature flag */ async getAuthEntities(enableCompanyFeature) {
|
|
104
104
|
const enable = enableCompanyFeature ?? this.getEnableCompanyFeature();
|
|
105
|
-
const { Company, CompanyBranch, User, UserCompanyPermission } = await import('../entities');
|
|
105
|
+
const { Company, CompanyBranch, User, UserCompanyPermission, UserToken, PendingRegistration } = await import('../entities');
|
|
106
106
|
const coreEntities = [
|
|
107
|
-
User
|
|
107
|
+
User,
|
|
108
|
+
UserToken,
|
|
109
|
+
PendingRegistration
|
|
108
110
|
];
|
|
109
111
|
const companyEntities = enable ? [
|
|
110
112
|
Company,
|