@flusys/nestjs-auth 1.0.0-beta → 1.0.0-rc
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 +274 -291
- package/cjs/config/auth.constants.js +12 -0
- package/cjs/controllers/authentication.controller.js +219 -10
- package/cjs/controllers/branch.controller.js +44 -1
- package/cjs/controllers/company-selection.controller.js +27 -20
- package/cjs/controllers/company.controller.js +44 -1
- package/cjs/controllers/user-permission.controller.js +40 -25
- package/cjs/controllers/user.controller.js +211 -7
- package/cjs/dtos/authentication.dto.js +119 -13
- package/cjs/dtos/user-permission.dto.js +28 -0
- package/cjs/dtos/user.dto.js +19 -4
- package/cjs/entities/index.js +5 -1
- package/cjs/entities/pending-registration.entity.js +125 -0
- package/cjs/entities/user-company-permission.entity.js +0 -6
- package/cjs/entities/user-token.entity.js +129 -0
- package/cjs/guards/auth-throttler.guard.js +53 -0
- package/cjs/interceptors/clear-token.interceptor.js +3 -6
- package/cjs/interceptors/set-token.interceptor.js +38 -31
- 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 -20
- package/cjs/services/auth-config.service.js +23 -2
- package/cjs/services/auth-datasource.provider.js +5 -3
- package/cjs/services/auth-email.service.js +471 -0
- package/cjs/services/authentication.service.js +169 -77
- package/cjs/services/branch.service.js +6 -5
- package/cjs/services/company-selection-session.service.js +1 -1
- package/cjs/services/index.js +1 -0
- package/cjs/services/user-permission.service.js +0 -2
- package/cjs/services/user.service.js +213 -34
- package/cjs/validators/password.validator.js +45 -0
- package/config/auth.constants.d.ts +3 -0
- package/controllers/authentication.controller.d.ts +13 -9
- package/controllers/company-selection.controller.d.ts +6 -2
- package/controllers/user.controller.d.ts +33 -0
- package/dtos/authentication.dto.d.ts +21 -3
- package/dtos/company-branch.dto.d.ts +0 -1
- package/dtos/company.dto.d.ts +0 -1
- package/dtos/user-permission.dto.d.ts +4 -0
- package/dtos/user.dto.d.ts +1 -2
- package/entities/index.d.ts +7 -3
- package/entities/pending-registration.entity.d.ts +17 -0
- package/entities/user-company-permission.entity.d.ts +0 -2
- package/entities/user-token.entity.d.ts +15 -0
- package/fesm/config/auth.constants.js +6 -2
- package/fesm/controllers/authentication.controller.js +221 -12
- package/fesm/controllers/branch.controller.js +45 -2
- package/fesm/controllers/company-selection.controller.js +28 -21
- package/fesm/controllers/company.controller.js +45 -2
- package/fesm/controllers/user-permission.controller.js +41 -26
- package/fesm/controllers/user.controller.js +214 -10
- package/fesm/dtos/authentication.dto.js +106 -14
- package/fesm/dtos/user-permission.dto.js +28 -0
- package/fesm/dtos/user.dto.js +20 -5
- package/fesm/entities/index.js +7 -1
- package/fesm/entities/pending-registration.entity.js +115 -0
- package/fesm/entities/user-company-permission.entity.js +0 -6
- package/fesm/entities/user-token.entity.js +113 -0
- package/fesm/guards/auth-throttler.guard.js +43 -0
- package/fesm/interceptors/clear-token.interceptor.js +2 -5
- package/fesm/interceptors/set-token.interceptor.js +37 -30
- 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 +10 -22
- package/fesm/services/auth-config.service.js +24 -3
- package/fesm/services/auth-datasource.provider.js +6 -4
- package/fesm/services/auth-email.service.js +420 -0
- package/fesm/services/authentication.service.js +169 -77
- package/fesm/services/branch.service.js +6 -5
- package/fesm/services/company-selection-session.service.js +1 -1
- package/fesm/services/index.js +1 -0
- package/fesm/services/user-permission.service.js +1 -3
- package/fesm/services/user.service.js +215 -36
- package/fesm/validators/password.validator.js +48 -0
- package/guards/auth-throttler.guard.d.ts +7 -0
- package/interceptors/set-token.interceptor.d.ts +1 -0
- 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 +64 -0
- package/interfaces/user.interface.d.ts +1 -0
- package/modules/auth.module.d.ts +0 -1
- package/package.json +4 -4
- package/services/auth-config.service.d.ts +5 -1
- package/services/auth-email.service.d.ts +51 -0
- package/services/authentication.service.d.ts +16 -11
- package/services/company-selection-session.service.d.ts +1 -1
- package/services/index.d.ts +1 -0
- package/services/user-permission.service.d.ts +0 -1
- package/services/user.service.d.ts +19 -3
- package/validators/password.validator.d.ts +10 -0
|
@@ -254,9 +254,23 @@ _ts_decorate([
|
|
|
254
254
|
], UserBranchPermissionDto.prototype, "companyId", void 0);
|
|
255
255
|
let UserCompaniesResponseDto = class UserCompaniesResponseDto {
|
|
256
256
|
constructor(){
|
|
257
|
+
_define_property(this, "success", void 0);
|
|
258
|
+
_define_property(this, "message", void 0);
|
|
257
259
|
_define_property(this, "data", void 0);
|
|
258
260
|
}
|
|
259
261
|
};
|
|
262
|
+
_ts_decorate([
|
|
263
|
+
(0, _swagger.ApiProperty)({
|
|
264
|
+
example: true
|
|
265
|
+
}),
|
|
266
|
+
_ts_metadata("design:type", Boolean)
|
|
267
|
+
], UserCompaniesResponseDto.prototype, "success", void 0);
|
|
268
|
+
_ts_decorate([
|
|
269
|
+
(0, _swagger.ApiProperty)({
|
|
270
|
+
example: 'User companies retrieved successfully'
|
|
271
|
+
}),
|
|
272
|
+
_ts_metadata("design:type", String)
|
|
273
|
+
], UserCompaniesResponseDto.prototype, "message", void 0);
|
|
260
274
|
_ts_decorate([
|
|
261
275
|
(0, _swagger.ApiProperty)({
|
|
262
276
|
description: 'List of companies the user has access to',
|
|
@@ -284,9 +298,23 @@ _ts_decorate([
|
|
|
284
298
|
], UserCompaniesResponseDto.prototype, "data", void 0);
|
|
285
299
|
let UserBranchesResponseDto = class UserBranchesResponseDto {
|
|
286
300
|
constructor(){
|
|
301
|
+
_define_property(this, "success", void 0);
|
|
302
|
+
_define_property(this, "message", void 0);
|
|
287
303
|
_define_property(this, "data", void 0);
|
|
288
304
|
}
|
|
289
305
|
};
|
|
306
|
+
_ts_decorate([
|
|
307
|
+
(0, _swagger.ApiProperty)({
|
|
308
|
+
example: true
|
|
309
|
+
}),
|
|
310
|
+
_ts_metadata("design:type", Boolean)
|
|
311
|
+
], UserBranchesResponseDto.prototype, "success", void 0);
|
|
312
|
+
_ts_decorate([
|
|
313
|
+
(0, _swagger.ApiProperty)({
|
|
314
|
+
example: 'User branches retrieved successfully'
|
|
315
|
+
}),
|
|
316
|
+
_ts_metadata("design:type", String)
|
|
317
|
+
], UserBranchesResponseDto.prototype, "message", void 0);
|
|
290
318
|
_ts_decorate([
|
|
291
319
|
(0, _swagger.ApiProperty)({
|
|
292
320
|
description: 'List of branches the user has access to',
|
package/cjs/dtos/user.dto.js
CHANGED
|
@@ -21,6 +21,7 @@ _export(exports, {
|
|
|
21
21
|
});
|
|
22
22
|
const _swagger = require("@nestjs/swagger");
|
|
23
23
|
const _classvalidator = require("class-validator");
|
|
24
|
+
const _passwordvalidator = require("../validators/password.validator");
|
|
24
25
|
function _define_property(obj, key, value) {
|
|
25
26
|
if (key in obj) {
|
|
26
27
|
Object.defineProperty(obj, key, {
|
|
@@ -83,10 +84,13 @@ _ts_decorate([
|
|
|
83
84
|
], CreateUserDto.prototype, "email", void 0);
|
|
84
85
|
_ts_decorate([
|
|
85
86
|
(0, _swagger.ApiProperty)({
|
|
86
|
-
example: '
|
|
87
|
-
description: 'Password'
|
|
87
|
+
example: 'SecureP@ss123',
|
|
88
|
+
description: 'Password (must meet complexity requirements)'
|
|
88
89
|
}),
|
|
89
90
|
(0, _classvalidator.IsString)(),
|
|
91
|
+
(0, _classvalidator.IsNotEmpty)(),
|
|
92
|
+
(0, _passwordvalidator.IsStrongPassword)(),
|
|
93
|
+
(0, _classvalidator.MaxLength)(128),
|
|
90
94
|
_ts_metadata("design:type", String)
|
|
91
95
|
], CreateUserDto.prototype, "password", void 0);
|
|
92
96
|
_ts_decorate([
|
|
@@ -139,6 +143,7 @@ let UpdateUserDto = class UpdateUserDto {
|
|
|
139
143
|
_define_property(this, "phone", void 0);
|
|
140
144
|
_define_property(this, "email", void 0);
|
|
141
145
|
_define_property(this, "password", void 0);
|
|
146
|
+
_define_property(this, "oldPassword", void 0);
|
|
142
147
|
_define_property(this, "profilePictureId", void 0);
|
|
143
148
|
_define_property(this, "isActive", void 0);
|
|
144
149
|
_define_property(this, "emailVerified", void 0);
|
|
@@ -150,7 +155,7 @@ _ts_decorate([
|
|
|
150
155
|
(0, _swagger.ApiProperty)({
|
|
151
156
|
description: 'User ID'
|
|
152
157
|
}),
|
|
153
|
-
(0, _classvalidator.
|
|
158
|
+
(0, _classvalidator.IsUUID)(),
|
|
154
159
|
_ts_metadata("design:type", String)
|
|
155
160
|
], UpdateUserDto.prototype, "id", void 0);
|
|
156
161
|
_ts_decorate([
|
|
@@ -182,12 +187,22 @@ _ts_decorate([
|
|
|
182
187
|
], UpdateUserDto.prototype, "email", void 0);
|
|
183
188
|
_ts_decorate([
|
|
184
189
|
(0, _swagger.ApiPropertyOptional)({
|
|
185
|
-
description: 'New password'
|
|
190
|
+
description: 'New password (must meet complexity requirements)'
|
|
186
191
|
}),
|
|
187
192
|
(0, _classvalidator.IsOptional)(),
|
|
188
193
|
(0, _classvalidator.IsString)(),
|
|
194
|
+
(0, _passwordvalidator.IsStrongPassword)(),
|
|
195
|
+
(0, _classvalidator.MaxLength)(128),
|
|
189
196
|
_ts_metadata("design:type", String)
|
|
190
197
|
], UpdateUserDto.prototype, "password", void 0);
|
|
198
|
+
_ts_decorate([
|
|
199
|
+
(0, _swagger.ApiPropertyOptional)({
|
|
200
|
+
description: 'Current password (required when changing password on own profile)'
|
|
201
|
+
}),
|
|
202
|
+
(0, _classvalidator.IsOptional)(),
|
|
203
|
+
(0, _classvalidator.IsString)(),
|
|
204
|
+
_ts_metadata("design:type", String)
|
|
205
|
+
], UpdateUserDto.prototype, "oldPassword", void 0);
|
|
191
206
|
_ts_decorate([
|
|
192
207
|
(0, _swagger.ApiPropertyOptional)({
|
|
193
208
|
description: 'Profile picture file ID'
|
package/cjs/entities/index.js
CHANGED
|
@@ -30,8 +30,10 @@ _export(exports, {
|
|
|
30
30
|
});
|
|
31
31
|
const _companybranchentity = _export_star(require("./company-branch.entity"), exports);
|
|
32
32
|
const _companyentity = _export_star(require("./company.entity"), exports);
|
|
33
|
+
const _pendingregistrationentity = _export_star(require("./pending-registration.entity"), exports);
|
|
33
34
|
const _usercompanypermissionentity = require("./user-company-permission.entity");
|
|
34
35
|
const _userentity = _export_star(require("./user.entity"), exports);
|
|
36
|
+
const _usertokenentity = _export_star(require("./user-token.entity"), exports);
|
|
35
37
|
function _export_star(from, to) {
|
|
36
38
|
Object.keys(from).forEach(function(k) {
|
|
37
39
|
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
@@ -46,7 +48,9 @@ function _export_star(from, to) {
|
|
|
46
48
|
return from;
|
|
47
49
|
}
|
|
48
50
|
const CoreEntities = [
|
|
49
|
-
_userentity.User
|
|
51
|
+
_userentity.User,
|
|
52
|
+
_usertokenentity.UserToken,
|
|
53
|
+
_pendingregistrationentity.PendingRegistration
|
|
50
54
|
];
|
|
51
55
|
const CompanyEntities = [
|
|
52
56
|
_companyentity.Company,
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "PendingRegistration", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return PendingRegistration;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _typeorm = require("typeorm");
|
|
12
|
+
function _define_property(obj, key, value) {
|
|
13
|
+
if (key in obj) {
|
|
14
|
+
Object.defineProperty(obj, key, {
|
|
15
|
+
value: value,
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true
|
|
19
|
+
});
|
|
20
|
+
} else {
|
|
21
|
+
obj[key] = value;
|
|
22
|
+
}
|
|
23
|
+
return obj;
|
|
24
|
+
}
|
|
25
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
26
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
27
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
28
|
+
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;
|
|
29
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
30
|
+
}
|
|
31
|
+
function _ts_metadata(k, v) {
|
|
32
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
33
|
+
}
|
|
34
|
+
let PendingRegistration = class PendingRegistration {
|
|
35
|
+
constructor(){
|
|
36
|
+
_define_property(this, "id", void 0);
|
|
37
|
+
_define_property(this, "email", void 0);
|
|
38
|
+
_define_property(this, "name", void 0);
|
|
39
|
+
_define_property(this, "phone", void 0);
|
|
40
|
+
_define_property(this, "passwordHash", void 0);
|
|
41
|
+
_define_property(this, "token", void 0);
|
|
42
|
+
_define_property(this, "expiresAt", void 0);
|
|
43
|
+
_define_property(this, "companyData", void 0);
|
|
44
|
+
_define_property(this, "createdAt", void 0);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
_ts_decorate([
|
|
48
|
+
(0, _typeorm.PrimaryGeneratedColumn)('uuid'),
|
|
49
|
+
_ts_metadata("design:type", String)
|
|
50
|
+
], PendingRegistration.prototype, "id", void 0);
|
|
51
|
+
_ts_decorate([
|
|
52
|
+
(0, _typeorm.Column)({
|
|
53
|
+
type: 'varchar',
|
|
54
|
+
length: 255
|
|
55
|
+
}),
|
|
56
|
+
_ts_metadata("design:type", String)
|
|
57
|
+
], PendingRegistration.prototype, "email", void 0);
|
|
58
|
+
_ts_decorate([
|
|
59
|
+
(0, _typeorm.Column)({
|
|
60
|
+
type: 'varchar',
|
|
61
|
+
length: 255,
|
|
62
|
+
nullable: true
|
|
63
|
+
}),
|
|
64
|
+
_ts_metadata("design:type", Object)
|
|
65
|
+
], PendingRegistration.prototype, "name", void 0);
|
|
66
|
+
_ts_decorate([
|
|
67
|
+
(0, _typeorm.Column)({
|
|
68
|
+
type: 'varchar',
|
|
69
|
+
length: 255,
|
|
70
|
+
nullable: true
|
|
71
|
+
}),
|
|
72
|
+
_ts_metadata("design:type", Object)
|
|
73
|
+
], PendingRegistration.prototype, "phone", void 0);
|
|
74
|
+
_ts_decorate([
|
|
75
|
+
(0, _typeorm.Column)({
|
|
76
|
+
type: 'text',
|
|
77
|
+
name: 'passwordHash'
|
|
78
|
+
}),
|
|
79
|
+
_ts_metadata("design:type", String)
|
|
80
|
+
], PendingRegistration.prototype, "passwordHash", void 0);
|
|
81
|
+
_ts_decorate([
|
|
82
|
+
(0, _typeorm.Column)({
|
|
83
|
+
type: 'varchar',
|
|
84
|
+
length: 64
|
|
85
|
+
}),
|
|
86
|
+
_ts_metadata("design:type", String)
|
|
87
|
+
], PendingRegistration.prototype, "token", void 0);
|
|
88
|
+
_ts_decorate([
|
|
89
|
+
(0, _typeorm.Column)({
|
|
90
|
+
type: 'timestamp',
|
|
91
|
+
name: 'expires_at'
|
|
92
|
+
}),
|
|
93
|
+
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
94
|
+
], PendingRegistration.prototype, "expiresAt", void 0);
|
|
95
|
+
_ts_decorate([
|
|
96
|
+
(0, _typeorm.Column)({
|
|
97
|
+
type: 'json',
|
|
98
|
+
nullable: true,
|
|
99
|
+
name: 'company_data'
|
|
100
|
+
}),
|
|
101
|
+
_ts_metadata("design:type", Object)
|
|
102
|
+
], PendingRegistration.prototype, "companyData", void 0);
|
|
103
|
+
_ts_decorate([
|
|
104
|
+
(0, _typeorm.CreateDateColumn)({
|
|
105
|
+
type: 'timestamp',
|
|
106
|
+
name: 'created_at'
|
|
107
|
+
}),
|
|
108
|
+
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
109
|
+
], PendingRegistration.prototype, "createdAt", void 0);
|
|
110
|
+
PendingRegistration = _ts_decorate([
|
|
111
|
+
(0, _typeorm.Entity)('pending_registrations'),
|
|
112
|
+
(0, _typeorm.Index)('IDX_pending_reg_email', [
|
|
113
|
+
'email'
|
|
114
|
+
], {
|
|
115
|
+
unique: true
|
|
116
|
+
}),
|
|
117
|
+
(0, _typeorm.Index)('IDX_pending_reg_token', [
|
|
118
|
+
'token'
|
|
119
|
+
], {
|
|
120
|
+
unique: true
|
|
121
|
+
}),
|
|
122
|
+
(0, _typeorm.Index)('IDX_pending_reg_expires', [
|
|
123
|
+
'expiresAt'
|
|
124
|
+
])
|
|
125
|
+
], PendingRegistration);
|
|
@@ -44,12 +44,6 @@ function _ts_metadata(k, v) {
|
|
|
44
44
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
45
45
|
}
|
|
46
46
|
let UserCompanyPermission = class UserCompanyPermission {
|
|
47
|
-
/* ---------- Helpers ---------- */ isCompanyPermission() {
|
|
48
|
-
return this.permissionType === _enums.PermissionType.COMPANY;
|
|
49
|
-
}
|
|
50
|
-
isBranchPermission() {
|
|
51
|
-
return this.permissionType === _enums.PermissionType.BRANCH;
|
|
52
|
-
}
|
|
53
47
|
constructor(){
|
|
54
48
|
_define_property(this, "id", void 0);
|
|
55
49
|
/* ---------- Ownership ---------- */ _define_property(this, "userId", void 0);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
get TokenType () {
|
|
13
|
+
return TokenType;
|
|
14
|
+
},
|
|
15
|
+
get UserToken () {
|
|
16
|
+
return UserToken;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const _typeorm = require("typeorm");
|
|
20
|
+
function _define_property(obj, key, value) {
|
|
21
|
+
if (key in obj) {
|
|
22
|
+
Object.defineProperty(obj, key, {
|
|
23
|
+
value: value,
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true
|
|
27
|
+
});
|
|
28
|
+
} else {
|
|
29
|
+
obj[key] = value;
|
|
30
|
+
}
|
|
31
|
+
return obj;
|
|
32
|
+
}
|
|
33
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
34
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
35
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
36
|
+
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;
|
|
37
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
38
|
+
}
|
|
39
|
+
function _ts_metadata(k, v) {
|
|
40
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
41
|
+
}
|
|
42
|
+
var TokenType = /*#__PURE__*/ function(TokenType) {
|
|
43
|
+
TokenType["PASSWORD_RESET"] = "password_reset";
|
|
44
|
+
TokenType["EMAIL_VERIFICATION"] = "email_verification";
|
|
45
|
+
TokenType["EMAIL_CHANGE"] = "email_change";
|
|
46
|
+
return TokenType;
|
|
47
|
+
}({});
|
|
48
|
+
let UserToken = class UserToken {
|
|
49
|
+
constructor(){
|
|
50
|
+
_define_property(this, "id", void 0);
|
|
51
|
+
_define_property(this, "userId", void 0);
|
|
52
|
+
_define_property(this, "type", void 0);
|
|
53
|
+
_define_property(this, "token", void 0);
|
|
54
|
+
_define_property(this, "expiresAt", void 0);
|
|
55
|
+
_define_property(this, "usedAt", void 0);
|
|
56
|
+
_define_property(this, "metadata", void 0);
|
|
57
|
+
_define_property(this, "createdAt", void 0);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
_ts_decorate([
|
|
61
|
+
(0, _typeorm.PrimaryGeneratedColumn)('uuid'),
|
|
62
|
+
_ts_metadata("design:type", String)
|
|
63
|
+
], UserToken.prototype, "id", void 0);
|
|
64
|
+
_ts_decorate([
|
|
65
|
+
(0, _typeorm.Column)({
|
|
66
|
+
type: 'uuid',
|
|
67
|
+
name: 'user_id'
|
|
68
|
+
}),
|
|
69
|
+
_ts_metadata("design:type", String)
|
|
70
|
+
], UserToken.prototype, "userId", void 0);
|
|
71
|
+
_ts_decorate([
|
|
72
|
+
(0, _typeorm.Column)({
|
|
73
|
+
type: 'varchar',
|
|
74
|
+
length: 32
|
|
75
|
+
}),
|
|
76
|
+
_ts_metadata("design:type", String)
|
|
77
|
+
], UserToken.prototype, "type", void 0);
|
|
78
|
+
_ts_decorate([
|
|
79
|
+
(0, _typeorm.Column)({
|
|
80
|
+
type: 'varchar',
|
|
81
|
+
length: 64
|
|
82
|
+
}),
|
|
83
|
+
_ts_metadata("design:type", String)
|
|
84
|
+
], UserToken.prototype, "token", void 0);
|
|
85
|
+
_ts_decorate([
|
|
86
|
+
(0, _typeorm.Column)({
|
|
87
|
+
type: 'timestamp',
|
|
88
|
+
name: 'expires_at'
|
|
89
|
+
}),
|
|
90
|
+
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
91
|
+
], UserToken.prototype, "expiresAt", void 0);
|
|
92
|
+
_ts_decorate([
|
|
93
|
+
(0, _typeorm.Column)({
|
|
94
|
+
type: 'timestamp',
|
|
95
|
+
name: 'used_at',
|
|
96
|
+
nullable: true
|
|
97
|
+
}),
|
|
98
|
+
_ts_metadata("design:type", Object)
|
|
99
|
+
], UserToken.prototype, "usedAt", void 0);
|
|
100
|
+
_ts_decorate([
|
|
101
|
+
(0, _typeorm.Column)({
|
|
102
|
+
type: 'varchar',
|
|
103
|
+
length: 255,
|
|
104
|
+
nullable: true
|
|
105
|
+
}),
|
|
106
|
+
_ts_metadata("design:type", Object)
|
|
107
|
+
], UserToken.prototype, "metadata", void 0);
|
|
108
|
+
_ts_decorate([
|
|
109
|
+
(0, _typeorm.CreateDateColumn)({
|
|
110
|
+
type: 'timestamp',
|
|
111
|
+
name: 'created_at'
|
|
112
|
+
}),
|
|
113
|
+
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
114
|
+
], UserToken.prototype, "createdAt", void 0);
|
|
115
|
+
UserToken = _ts_decorate([
|
|
116
|
+
(0, _typeorm.Entity)('user_tokens'),
|
|
117
|
+
(0, _typeorm.Index)('IDX_user_token_token', [
|
|
118
|
+
'token'
|
|
119
|
+
], {
|
|
120
|
+
unique: true
|
|
121
|
+
}),
|
|
122
|
+
(0, _typeorm.Index)('IDX_user_token_user_type', [
|
|
123
|
+
'userId',
|
|
124
|
+
'type'
|
|
125
|
+
]),
|
|
126
|
+
(0, _typeorm.Index)('IDX_user_token_expires', [
|
|
127
|
+
'expiresAt'
|
|
128
|
+
])
|
|
129
|
+
], UserToken);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "AuthThrottlerGuard", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return AuthThrottlerGuard;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _common = require("@nestjs/common");
|
|
12
|
+
const _throttler = require("@nestjs/throttler");
|
|
13
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
14
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
15
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
16
|
+
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;
|
|
17
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
18
|
+
}
|
|
19
|
+
let AuthThrottlerGuard = class AuthThrottlerGuard extends _throttler.ThrottlerGuard {
|
|
20
|
+
/**
|
|
21
|
+
* Generate tracker key combining IP and email for login attempts.
|
|
22
|
+
* This prevents attackers from brute-forcing multiple accounts from the same IP.
|
|
23
|
+
*/ async getTracker(req) {
|
|
24
|
+
const ip = req.ip || req.headers?.['x-forwarded-for'] || 'unknown';
|
|
25
|
+
const body = req.body;
|
|
26
|
+
const email = body?.email || '';
|
|
27
|
+
return `auth:${ip}:${email}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Custom error message for rate limit exceeded.
|
|
31
|
+
*/ throwThrottlingException() {
|
|
32
|
+
throw new _throttler.ThrottlerException('Too many authentication attempts. Please try again later.');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Skip rate limiting for authenticated users on certain endpoints.
|
|
36
|
+
*/ async shouldSkip(context) {
|
|
37
|
+
const request = context.switchToHttp().getRequest();
|
|
38
|
+
const path = request.path || request.url || '';
|
|
39
|
+
// Remove query string for comparison
|
|
40
|
+
const pathOnly = path.split('?')[0];
|
|
41
|
+
// Skip rate limiting for authenticated endpoints (they have their own protection)
|
|
42
|
+
// Use exact match or prefix match to prevent bypass via path manipulation
|
|
43
|
+
const authenticatedEndpoints = [
|
|
44
|
+
'/auth/me',
|
|
45
|
+
'/auth/logout',
|
|
46
|
+
'/auth/change-password'
|
|
47
|
+
];
|
|
48
|
+
return authenticatedEndpoints.some((endpoint)=>pathOnly === endpoint || pathOnly.startsWith(`${endpoint}/`));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
AuthThrottlerGuard = _ts_decorate([
|
|
52
|
+
(0, _common.Injectable)()
|
|
53
|
+
], AuthThrottlerGuard);
|
|
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "ClearToken", {
|
|
|
10
10
|
});
|
|
11
11
|
const _common = require("@nestjs/common");
|
|
12
12
|
const _rxjs = require("rxjs");
|
|
13
|
+
const _config = require("../config");
|
|
13
14
|
const _authconfigservice = require("../services/auth-config.service");
|
|
14
15
|
function _define_property(obj, key, value) {
|
|
15
16
|
if (key in obj) {
|
|
@@ -38,23 +39,19 @@ function _ts_param(paramIndex, decorator) {
|
|
|
38
39
|
decorator(target, key, paramIndex);
|
|
39
40
|
};
|
|
40
41
|
}
|
|
41
|
-
const DEFAULT_COOKIE_NAME = 'fsn_refresh_token';
|
|
42
42
|
let ClearToken = class ClearToken {
|
|
43
43
|
get cookieName() {
|
|
44
|
-
return this.authConfig?.getRefreshTokenCookieName() ?? DEFAULT_COOKIE_NAME;
|
|
44
|
+
return this.authConfig?.getRefreshTokenCookieName() ?? _config.DEFAULT_COOKIE_NAME;
|
|
45
45
|
}
|
|
46
46
|
intercept(context, next) {
|
|
47
47
|
const req = context.switchToHttp().getRequest();
|
|
48
48
|
const res = context.switchToHttp().getResponse();
|
|
49
49
|
return next.handle().pipe((0, _rxjs.map)((value)=>{
|
|
50
|
-
// Clear the refresh token cookie
|
|
51
50
|
this.clearRefreshTokenCookie(req, res);
|
|
52
51
|
return value;
|
|
53
52
|
}));
|
|
54
53
|
}
|
|
55
|
-
|
|
56
|
-
* Clear refresh token cookie
|
|
57
|
-
*/ clearRefreshTokenCookie(req, res) {
|
|
54
|
+
clearRefreshTokenCookie(req, res) {
|
|
58
55
|
const origin = req.headers.origin || '';
|
|
59
56
|
const hostname = req.hostname || '';
|
|
60
57
|
const isHttps = origin.startsWith('https://') || req.secure;
|
|
@@ -11,6 +11,7 @@ Object.defineProperty(exports, "SetToken", {
|
|
|
11
11
|
const _common = require("@nestjs/common");
|
|
12
12
|
const _buffer = require("buffer");
|
|
13
13
|
const _rxjs = require("rxjs");
|
|
14
|
+
const _config = require("../config");
|
|
14
15
|
const _authconfigservice = require("../services/auth-config.service");
|
|
15
16
|
function _define_property(obj, key, value) {
|
|
16
17
|
if (key in obj) {
|
|
@@ -39,80 +40,85 @@ function _ts_param(paramIndex, decorator) {
|
|
|
39
40
|
decorator(target, key, paramIndex);
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
|
-
const DEFAULT_COOKIE_NAME = 'fsn_refresh_token';
|
|
43
|
-
const MAX_COOKIE_BYTES = 3500;
|
|
44
43
|
let SetToken = class SetToken {
|
|
45
44
|
get cookieName() {
|
|
46
|
-
return this.authConfig?.getRefreshTokenCookieName() ?? DEFAULT_COOKIE_NAME;
|
|
45
|
+
return this.authConfig?.getRefreshTokenCookieName() ?? _config.DEFAULT_COOKIE_NAME;
|
|
46
|
+
}
|
|
47
|
+
/** Convert expiration string (e.g., '7d', '24h') to milliseconds */ getMaxAgeMs() {
|
|
48
|
+
const expiry = this.authConfig?.getRefreshTokenExpiration() ?? '7d';
|
|
49
|
+
const match = expiry.match(/^(\d+)(s|m|h|d|w)$/);
|
|
50
|
+
if (!match) {
|
|
51
|
+
return 7 * 24 * 60 * 60 * 1000; // Default 7 days
|
|
52
|
+
}
|
|
53
|
+
const value = parseInt(match[1], 10);
|
|
54
|
+
const unit = match[2];
|
|
55
|
+
const multipliers = {
|
|
56
|
+
s: 1000,
|
|
57
|
+
m: 60 * 1000,
|
|
58
|
+
h: 60 * 60 * 1000,
|
|
59
|
+
d: 24 * 60 * 60 * 1000,
|
|
60
|
+
w: 7 * 24 * 60 * 60 * 1000
|
|
61
|
+
};
|
|
62
|
+
return value * (multipliers[unit] || multipliers['d']);
|
|
47
63
|
}
|
|
48
64
|
intercept(context, next) {
|
|
49
65
|
const req = context.switchToHttp().getRequest();
|
|
50
66
|
const res = context.switchToHttp().getResponse();
|
|
51
67
|
return next.handle().pipe((0, _rxjs.map)((value)=>{
|
|
52
|
-
// Skip if no refreshToken in response
|
|
53
68
|
if (!value || !value.refreshToken) {
|
|
54
69
|
return value;
|
|
55
70
|
}
|
|
56
71
|
const isBrowser = this.isBrowserRequest(req);
|
|
57
72
|
const tokenValue = value.refreshToken;
|
|
58
73
|
if (isBrowser) {
|
|
59
|
-
// Browser: Set cookie and remove from response body
|
|
60
74
|
this.setRefreshTokenCookie(req, res, tokenValue);
|
|
61
|
-
// Return response without refreshToken for browsers
|
|
62
75
|
const { refreshToken, ...responseWithoutToken } = value;
|
|
63
76
|
return responseWithoutToken;
|
|
64
77
|
}
|
|
65
|
-
// API/Mobile: Keep refreshToken in response body
|
|
66
78
|
return value;
|
|
67
79
|
}), (0, _rxjs.catchError)((err)=>(0, _rxjs.throwError)(()=>err)));
|
|
68
80
|
}
|
|
69
|
-
|
|
70
|
-
* Detect if request is from a browser
|
|
71
|
-
* Checks Accept header and custom X-Client-Type header
|
|
72
|
-
*/ isBrowserRequest(req) {
|
|
73
|
-
// Check custom header first (allows explicit client identification)
|
|
81
|
+
isBrowserRequest(req) {
|
|
74
82
|
const clientType = req.headers['x-client-type'];
|
|
75
83
|
if (clientType) {
|
|
76
84
|
return clientType.toLowerCase() === 'browser' || clientType.toLowerCase() === 'web';
|
|
77
85
|
}
|
|
78
|
-
// Check Accept header - browsers typically include text/html
|
|
79
86
|
const accept = req.headers['accept'] || '';
|
|
80
87
|
if (accept.includes('text/html')) {
|
|
81
88
|
return true;
|
|
82
89
|
}
|
|
83
|
-
// Check User-Agent for common browsers
|
|
84
90
|
const userAgent = req.headers['user-agent'] || '';
|
|
85
91
|
const browserPatterns = /mozilla|chrome|safari|firefox|edge|opera|msie/i;
|
|
86
|
-
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
return false;
|
|
92
|
+
return browserPatterns.test(userAgent) && !userAgent.includes('Postman');
|
|
90
93
|
}
|
|
91
|
-
|
|
92
|
-
* Set refresh token in HTTP-only cookie
|
|
93
|
-
*/ setRefreshTokenCookie(req, res, tokenValue) {
|
|
94
|
-
const origin = req.headers.origin || '';
|
|
94
|
+
setRefreshTokenCookie(req, res, tokenValue) {
|
|
95
95
|
const hostname = req.hostname || '';
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
// Determine if we're in production - always use secure cookies in production
|
|
97
|
+
// This handles TLS-terminating proxies correctly (they set x-forwarded-proto)
|
|
98
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
99
|
+
const forwardedProto = req.headers['x-forwarded-proto'];
|
|
100
|
+
const isHttps = isProduction || forwardedProto === 'https' || req.secure;
|
|
101
|
+
// Set cookie domain - only for subdomains with 3+ parts
|
|
102
|
+
// For security, limit to same domain unless explicitly configured
|
|
103
|
+
let cookieDomain;
|
|
98
104
|
const domainParts = hostname.split('.');
|
|
99
|
-
if (domainParts.length > 2) {
|
|
105
|
+
if (domainParts.length > 2 && !hostname.includes('localhost')) {
|
|
100
106
|
cookieDomain = '.' + domainParts.slice(-2).join('.');
|
|
101
107
|
}
|
|
102
|
-
|
|
103
|
-
const rawCookieString = `${this.cookieName}=${tokenValue}; HttpOnly; Domain=${cookieDomain}`;
|
|
108
|
+
const rawCookieString = `${this.cookieName}=${tokenValue}; HttpOnly`;
|
|
104
109
|
const estimatedBytes = _buffer.Buffer.byteLength(rawCookieString, 'utf8');
|
|
105
|
-
if (estimatedBytes > MAX_COOKIE_BYTES) {
|
|
110
|
+
if (estimatedBytes > _config.MAX_COOKIE_BYTES) {
|
|
106
111
|
throw new _common.PayloadTooLargeException(`Refresh token too large (≈${estimatedBytes} bytes)`);
|
|
107
112
|
}
|
|
108
|
-
// Set HTTP-only secure cookie
|
|
109
113
|
res.cookie(this.cookieName, tokenValue, {
|
|
110
114
|
httpOnly: true,
|
|
111
115
|
secure: isHttps,
|
|
112
116
|
sameSite: isHttps ? 'strict' : 'lax',
|
|
113
|
-
|
|
117
|
+
...cookieDomain && {
|
|
118
|
+
domain: cookieDomain
|
|
119
|
+
},
|
|
114
120
|
path: '/',
|
|
115
|
-
maxAge:
|
|
121
|
+
maxAge: this.getMaxAgeMs()
|
|
116
122
|
});
|
|
117
123
|
}
|
|
118
124
|
constructor(authConfig){
|
|
@@ -123,6 +129,7 @@ let SetToken = class SetToken {
|
|
|
123
129
|
SetToken = _ts_decorate([
|
|
124
130
|
(0, _common.Injectable)(),
|
|
125
131
|
_ts_param(0, (0, _common.Optional)()),
|
|
132
|
+
_ts_param(0, (0, _common.Inject)(_authconfigservice.AuthConfigService)),
|
|
126
133
|
_ts_metadata("design:type", Function),
|
|
127
134
|
_ts_metadata("design:paramtypes", [
|
|
128
135
|
typeof _authconfigservice.AuthConfigService === "undefined" ? Object : _authconfigservice.AuthConfigService
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
*/ "use strict";
|
|
36
|
+
Object.defineProperty(exports, "__esModule", {
|
|
37
|
+
value: true
|
|
38
|
+
});
|
|
39
|
+
Object.defineProperty(exports, "AUTH_EMAIL_PROVIDER", {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
get: function() {
|
|
42
|
+
return AUTH_EMAIL_PROVIDER;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const AUTH_EMAIL_PROVIDER = 'AUTH_EMAIL_PROVIDER';
|
package/cjs/interfaces/index.js
CHANGED
|
@@ -3,9 +3,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
5
|
_export_star(require("./authentication.interface"), exports);
|
|
6
|
+
_export_star(require("./auth-email-provider.interface"), exports);
|
|
6
7
|
_export_star(require("./company-branch.interface"), exports);
|
|
7
8
|
_export_star(require("./company.interface"), exports);
|
|
8
9
|
_export_star(require("./user.interface"), exports);
|
|
10
|
+
_export_star(require("./user-enricher.interface"), exports);
|
|
9
11
|
_export_star(require("./auth-module-options.interface"), exports);
|
|
10
12
|
function _export_star(from, to) {
|
|
11
13
|
Object.keys(from).forEach(function(k) {
|