@ackplus/nest-auth 1.1.15 → 1.1.16

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.
Files changed (28) hide show
  1. package/README.md +82 -6
  2. package/package.json +1 -1
  3. package/src/lib/admin-console/static/index.html +4736 -432
  4. package/src/lib/auth/auth.module.d.ts.map +1 -1
  5. package/src/lib/auth/auth.module.js +2 -0
  6. package/src/lib/auth/controllers/auth.controller.d.ts +1 -0
  7. package/src/lib/auth/controllers/auth.controller.d.ts.map +1 -1
  8. package/src/lib/auth/controllers/auth.controller.js +19 -2
  9. package/src/lib/auth/dto/requests/verify-2fa.request.dto.d.ts +1 -0
  10. package/src/lib/auth/dto/requests/verify-2fa.request.dto.d.ts.map +1 -1
  11. package/src/lib/auth/dto/requests/verify-2fa.request.dto.js +8 -0
  12. package/src/lib/auth/entities/trusted-device.entity.d.ts +13 -0
  13. package/src/lib/auth/entities/trusted-device.entity.d.ts.map +1 -0
  14. package/src/lib/auth/entities/trusted-device.entity.js +51 -0
  15. package/src/lib/auth/index.d.ts +1 -0
  16. package/src/lib/auth/index.d.ts.map +1 -1
  17. package/src/lib/auth/index.js +1 -0
  18. package/src/lib/auth/services/auth.service.d.ts +1 -0
  19. package/src/lib/auth/services/auth.service.d.ts.map +1 -1
  20. package/src/lib/auth/services/auth.service.js +29 -1
  21. package/src/lib/auth/services/mfa.service.d.ts +5 -1
  22. package/src/lib/auth/services/mfa.service.d.ts.map +1 -1
  23. package/src/lib/auth/services/mfa.service.js +40 -1
  24. package/src/lib/auth.constants.d.ts +1 -0
  25. package/src/lib/auth.constants.d.ts.map +1 -1
  26. package/src/lib/auth.constants.js +2 -1
  27. package/src/lib/core/interfaces/mfa-options.interface.d.ts +2 -0
  28. package/src/lib/core/interfaces/mfa-options.interface.d.ts.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"auth.module.d.ts","sourceRoot":"","sources":["../../../../../../packages/nest-auth/src/lib/auth/auth.module.ts"],"names":[],"mappings":"AAsBA,qBAkCa,UAAU;CACtB"}
1
+ {"version":3,"file":"auth.module.d.ts","sourceRoot":"","sources":["../../../../../../packages/nest-auth/src/lib/auth/auth.module.ts"],"names":[],"mappings":"AAuBA,qBAmCa,UAAU;CACtB"}
@@ -17,6 +17,7 @@ const user_entity_1 = require("../user/entities/user.entity");
17
17
  const otp_entity_1 = require("./entities/otp.entity");
18
18
  const mfa_secret_entity_1 = require("./entities/mfa-secret.entity");
19
19
  const access_key_entity_1 = require("../user/entities/access-key.entity");
20
+ const trusted_device_entity_1 = require("./entities/trusted-device.entity");
20
21
  const event_emitter_1 = require("@nestjs/event-emitter");
21
22
  const user_module_1 = require("../user/user.module");
22
23
  const core_module_1 = require("../core/core.module");
@@ -36,6 +37,7 @@ exports.AuthModule = AuthModule = tslib_1.__decorate([
36
37
  mfa_secret_entity_1.NestAuthMFASecret,
37
38
  access_key_entity_1.NestAuthAccessKey,
38
39
  identity_entity_1.NestAuthIdentity,
40
+ trusted_device_entity_1.NestAuthTrustedDevice,
39
41
  ]),
40
42
  (0, common_1.forwardRef)(() => core_module_1.CoreModule),
41
43
  (0, common_1.forwardRef)(() => user_module_1.UserModule),
@@ -43,6 +43,7 @@ export declare class AuthController {
43
43
  protected handle2faResponse(res: Response, authResult: {
44
44
  accessToken: string;
45
45
  refreshToken: string;
46
+ trustToken?: string;
46
47
  }): void;
47
48
  signup(input: SignupRequestDto, res: Response): Promise<void>;
48
49
  login(input: LoginRequestDto, res: Response): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.controller.d.ts","sourceRoot":"","sources":["../../../../../../../packages/nest-auth/src/lib/auth/controllers/auth.controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.controller.d.ts","sourceRoot":"","sources":["../../../../../../../packages/nest-auth/src/lib/auth/controllers/auth.controller.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,7 @@ const send_email_verification_request_dto_1 = require("../dto/requests/send-emai
27
27
  const verify_email_request_dto_1 = require("../dto/requests/verify-email.request.dto");
28
28
  const client_config_service_1 = require("../services/client-config.service");
29
29
  const client_config_response_dto_1 = require("../dto/responses/client-config.response.dto");
30
+ const auth_constants_1 = require("../../auth.constants");
30
31
  let AuthController = class AuthController {
31
32
  constructor(authService, cookieService, authConfig, clientConfigService) {
32
33
  this.authService = authService;
@@ -69,12 +70,27 @@ let AuthController = class AuthController {
69
70
  * Handle 2FA verification response based on configuration
70
71
  */
71
72
  handle2faResponse(res, authResult) {
73
+ if (authResult.trustToken) {
74
+ const trustCookieName = auth_config_service_1.AuthConfigService.getOptions().mfa?.trustDeviceStorageName || auth_constants_1.NEST_AUTH_TRUST_DEVICE_KEY;
75
+ const duration = auth_config_service_1.AuthConfigService.getOptions().mfa?.trustedDeviceDuration || '30d';
76
+ // Convert duration to milliseconds for maxAge
77
+ const ms = require('ms');
78
+ const maxAge = typeof duration === 'string' ? ms(duration) : duration;
79
+ res.cookie(trustCookieName, authResult.trustToken, {
80
+ httpOnly: true,
81
+ secure: process.env.NODE_ENV === 'production',
82
+ sameSite: 'lax',
83
+ path: '/',
84
+ maxAge,
85
+ });
86
+ }
72
87
  if (this.isUsingCookies()) {
73
88
  // Cookie mode: Set tokens in cookies, return success message
74
89
  this.cookieService.setTokens(res, authResult.accessToken, authResult.refreshToken);
75
90
  res.status(200).json({
76
- message: '2FA verification successful'
91
+ message: '2FA verification successful',
77
92
  // Note: No isRequiresMfa for 2FA verification (it's already verified)
93
+ trustToken: authResult.trustToken // Return trust token for mobile apps even in cookie mode
78
94
  });
79
95
  }
80
96
  else {
@@ -82,7 +98,8 @@ let AuthController = class AuthController {
82
98
  res.status(200).json({
83
99
  message: '2FA verification successful',
84
100
  accessToken: authResult.accessToken,
85
- refreshToken: authResult.refreshToken
101
+ refreshToken: authResult.refreshToken,
102
+ trustToken: authResult.trustToken
86
103
  });
87
104
  }
88
105
  }
@@ -2,5 +2,6 @@ import { MFAMethodEnum } from "../../../core";
2
2
  export declare class Verify2faRequestDto {
3
3
  method: MFAMethodEnum;
4
4
  otp: string;
5
+ rememberDevice?: boolean;
5
6
  }
6
7
  //# sourceMappingURL=verify-2fa.request.dto.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"verify-2fa.request.dto.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/nest-auth/src/lib/auth/dto/requests/verify-2fa.request.dto.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,qBAAa,mBAAmB;IAQ5B,MAAM,EAAE,aAAa,CAAC;IAUtB,GAAG,EAAE,MAAM,CAAC;CACf"}
1
+ {"version":3,"file":"verify-2fa.request.dto.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/nest-auth/src/lib/auth/dto/requests/verify-2fa.request.dto.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,qBAAa,mBAAmB;IAQ5B,MAAM,EAAE,aAAa,CAAC;IAUtB,GAAG,EAAE,MAAM,CAAC;IAOZ,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B"}
@@ -29,3 +29,11 @@ tslib_1.__decorate([
29
29
  (0, class_validator_1.IsNotEmpty)(),
30
30
  tslib_1.__metadata("design:type", String)
31
31
  ], Verify2faRequestDto.prototype, "otp", void 0);
32
+ tslib_1.__decorate([
33
+ (0, swagger_1.ApiProperty)({
34
+ description: 'Whether to trust this device for future logins',
35
+ example: true,
36
+ required: false,
37
+ }),
38
+ tslib_1.__metadata("design:type", Boolean)
39
+ ], Verify2faRequestDto.prototype, "rememberDevice", void 0);
@@ -0,0 +1,13 @@
1
+ import { NestAuthUser } from '../../user/entities/user.entity';
2
+ export declare class NestAuthTrustedDevice {
3
+ id: string;
4
+ userId: string;
5
+ user: NestAuthUser;
6
+ token: string;
7
+ userAgent: string;
8
+ ipAddress: string;
9
+ expiresAt: Date;
10
+ lastUsedAt: Date;
11
+ createdAt: Date;
12
+ }
13
+ //# sourceMappingURL=trusted-device.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trusted-device.entity.d.ts","sourceRoot":"","sources":["../../../../../../../packages/nest-auth/src/lib/auth/entities/trusted-device.entity.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAE/D,qBACa,qBAAqB;IAE9B,EAAE,EAAE,MAAM,CAAC;IAIX,MAAM,EAAE,MAAM,CAAC;IAIf,IAAI,EAAE,YAAY,CAAC;IAInB,KAAK,EAAE,MAAM,CAAC;IAGd,SAAS,EAAE,MAAM,CAAC;IAGlB,SAAS,EAAE,MAAM,CAAC;IAGlB,SAAS,EAAE,IAAI,CAAC;IAGhB,UAAU,EAAE,IAAI,CAAC;IAGjB,SAAS,EAAE,IAAI,CAAC;CACnB"}
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NestAuthTrustedDevice = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const typeorm_1 = require("typeorm");
6
+ const user_entity_1 = require("../../user/entities/user.entity");
7
+ let NestAuthTrustedDevice = class NestAuthTrustedDevice {
8
+ };
9
+ exports.NestAuthTrustedDevice = NestAuthTrustedDevice;
10
+ tslib_1.__decorate([
11
+ (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
12
+ tslib_1.__metadata("design:type", String)
13
+ ], NestAuthTrustedDevice.prototype, "id", void 0);
14
+ tslib_1.__decorate([
15
+ (0, typeorm_1.Column)(),
16
+ (0, typeorm_1.Index)(),
17
+ tslib_1.__metadata("design:type", String)
18
+ ], NestAuthTrustedDevice.prototype, "userId", void 0);
19
+ tslib_1.__decorate([
20
+ (0, typeorm_1.ManyToOne)(() => user_entity_1.NestAuthUser, { onDelete: 'CASCADE' }),
21
+ (0, typeorm_1.JoinColumn)({ name: 'userId' }),
22
+ tslib_1.__metadata("design:type", user_entity_1.NestAuthUser)
23
+ ], NestAuthTrustedDevice.prototype, "user", void 0);
24
+ tslib_1.__decorate([
25
+ (0, typeorm_1.Column)(),
26
+ (0, typeorm_1.Index)(),
27
+ tslib_1.__metadata("design:type", String)
28
+ ], NestAuthTrustedDevice.prototype, "token", void 0);
29
+ tslib_1.__decorate([
30
+ (0, typeorm_1.Column)({ nullable: true }),
31
+ tslib_1.__metadata("design:type", String)
32
+ ], NestAuthTrustedDevice.prototype, "userAgent", void 0);
33
+ tslib_1.__decorate([
34
+ (0, typeorm_1.Column)({ nullable: true }),
35
+ tslib_1.__metadata("design:type", String)
36
+ ], NestAuthTrustedDevice.prototype, "ipAddress", void 0);
37
+ tslib_1.__decorate([
38
+ (0, typeorm_1.Column)(),
39
+ tslib_1.__metadata("design:type", Date)
40
+ ], NestAuthTrustedDevice.prototype, "expiresAt", void 0);
41
+ tslib_1.__decorate([
42
+ (0, typeorm_1.Column)({ nullable: true }),
43
+ tslib_1.__metadata("design:type", Date)
44
+ ], NestAuthTrustedDevice.prototype, "lastUsedAt", void 0);
45
+ tslib_1.__decorate([
46
+ (0, typeorm_1.CreateDateColumn)(),
47
+ tslib_1.__metadata("design:type", Date)
48
+ ], NestAuthTrustedDevice.prototype, "createdAt", void 0);
49
+ exports.NestAuthTrustedDevice = NestAuthTrustedDevice = tslib_1.__decorate([
50
+ (0, typeorm_1.Entity)('nest_auth_trusted_devices')
51
+ ], NestAuthTrustedDevice);
@@ -38,4 +38,5 @@ export * from './dto/responses/mfa-status.response.dto';
38
38
  export * from './dto/responses/client-config.response.dto';
39
39
  export * from './entities/otp.entity';
40
40
  export * from './entities/mfa-secret.entity';
41
+ export * from './entities/trusted-device.entity';
41
42
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../packages/nest-auth/src/lib/auth/index.ts"],"names":[],"mappings":"AACA,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,cAAc,0CAA0C,CAAC;AAGzD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yCAAyC,CAAC;AACxD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,qCAAqC,CAAC;AAGpD,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,kCAAkC,CAAC;AAGjD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAG7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,0CAA0C,CAAC;AACzD,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,sDAAsD,CAAC;AACrE,cAAc,uDAAuD,CAAC;AACtE,cAAc,0CAA0C,CAAC;AACzD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,uCAAuC,CAAC;AACtD,cAAc,0CAA0C,CAAC;AACzD,cAAc,uCAAuC,CAAC;AACtD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,mCAAmC,CAAC;AAClD,cAAc,0CAA0C,CAAC;AACzD,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,4CAA4C,CAAC;AAG3D,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../packages/nest-auth/src/lib/auth/index.ts"],"names":[],"mappings":"AACA,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,cAAc,0CAA0C,CAAC;AAGzD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yCAAyC,CAAC;AACxD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,qCAAqC,CAAC;AAGpD,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,kCAAkC,CAAC;AAGjD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAG7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,0CAA0C,CAAC;AACzD,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,sDAAsD,CAAC;AACrE,cAAc,uDAAuD,CAAC;AACtE,cAAc,0CAA0C,CAAC;AACzD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,uCAAuC,CAAC;AACtD,cAAc,0CAA0C,CAAC;AACzD,cAAc,uCAAuC,CAAC;AACtD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,mCAAmC,CAAC;AAClD,cAAc,0CAA0C,CAAC;AACzD,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,4CAA4C,CAAC;AAG3D,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC"}
@@ -50,3 +50,4 @@ tslib_1.__exportStar(require("./dto/responses/client-config.response.dto"), expo
50
50
  // Entities
51
51
  tslib_1.__exportStar(require("./entities/otp.entity"), exports);
52
52
  tslib_1.__exportStar(require("./entities/mfa-secret.entity"), exports);
53
+ tslib_1.__exportStar(require("./entities/trusted-device.entity"), exports);
@@ -41,6 +41,7 @@ export declare class AuthService {
41
41
  verify2fa(input: Verify2faRequestDto): Promise<{
42
42
  accessToken: string;
43
43
  refreshToken: string;
44
+ trustToken: string;
44
45
  }>;
45
46
  send2faCode(userId: string, method: MFAMethodEnum): Promise<boolean>;
46
47
  private handleSocialLogin;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/nest-auth/src/lib/auth/services/auth.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/nest-auth/src/lib/auth/services/auth.service.ts"],"names":[],"mappings":"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"}
@@ -174,7 +174,27 @@ let AuthService = class AuthService {
174
174
  user = await this.getUserWithRolesAndPermissions(user.id);
175
175
  const session = await this.sessionManager.createSessionFromUser(user);
176
176
  const tokens = await this.generateTokensFromSession(session);
177
- const isRequiresMfa = await this.mfaService.isRequiresMfa(user.id);
177
+ let isRequiresMfa = await this.mfaService.isRequiresMfa(user.id);
178
+ // Check for trusted device cookie or header if MFA is required
179
+ if (isRequiresMfa) {
180
+ const trustCookieName = auth_config_service_1.AuthConfigService.getOptions().mfa?.trustDeviceStorageName || auth_constants_1.NEST_AUTH_TRUST_DEVICE_KEY;
181
+ const req = request_context_1.RequestContext.currentRequest();
182
+ let trustToken = req.cookies?.[trustCookieName];
183
+ // If not in cookie, check header
184
+ if (!trustToken) {
185
+ trustToken = req.headers[trustCookieName];
186
+ }
187
+ if (trustToken) {
188
+ const isTrusted = await this.mfaService.validateTrustedDevice(user.id, trustToken);
189
+ if (isTrusted) {
190
+ isRequiresMfa = false;
191
+ // Update session to indicate MFA is verified by trust
192
+ await this.sessionManager.updateSession(session.id, {
193
+ data: { ...session.data, isMfaVerified: true }
194
+ });
195
+ }
196
+ }
197
+ }
178
198
  // Emit login event
179
199
  await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.LOGGED_IN, new user_logged_in_event_1.UserLoggedInEvent({
180
200
  user,
@@ -219,6 +239,13 @@ let AuthService = class AuthService {
219
239
  }
220
240
  });
221
241
  const tokens = await this.generateTokensFromSession(payload);
242
+ let trustToken;
243
+ if (input.rememberDevice) {
244
+ const req = request_context_1.RequestContext.currentRequest();
245
+ const userAgent = req.headers['user-agent'] || '';
246
+ const ip = req.ip || req.socket.remoteAddress || '';
247
+ trustToken = await this.mfaService.createTrustedDevice(session.userId, userAgent, ip);
248
+ }
222
249
  const user = await this.getUser();
223
250
  // Emit 2FA verified event
224
251
  this.debugLogger.debug('Emitting 2FA verified event', 'AuthService', { userId: user.id });
@@ -233,6 +260,7 @@ let AuthService = class AuthService {
233
260
  return {
234
261
  accessToken: tokens.accessToken,
235
262
  refreshToken: tokens.refreshToken,
263
+ trustToken,
236
264
  };
237
265
  }
238
266
  catch (error) {
@@ -4,13 +4,15 @@ import { MFAMethodEnum, MFAOptions } from '../../core/interfaces/mfa-options.int
4
4
  import { NestAuthUser } from '../../user/entities/user.entity';
5
5
  import { NestAuthOTP } from '../../auth/entities/otp.entity';
6
6
  import { EventEmitter2 } from '@nestjs/event-emitter';
7
+ import { NestAuthTrustedDevice } from '../entities/trusted-device.entity';
7
8
  export declare class MfaService {
8
9
  private mfaSecretRepository;
9
10
  private userRepository;
10
11
  private otpRepository;
12
+ private trustedDeviceRepository;
11
13
  private eventEmitter;
12
14
  mfaConfig: MFAOptions;
13
- constructor(mfaSecretRepository: Repository<NestAuthMFASecret>, userRepository: Repository<NestAuthUser>, otpRepository: Repository<NestAuthOTP>, eventEmitter: EventEmitter2);
15
+ constructor(mfaSecretRepository: Repository<NestAuthMFASecret>, userRepository: Repository<NestAuthUser>, otpRepository: Repository<NestAuthOTP>, trustedDeviceRepository: Repository<NestAuthTrustedDevice>, eventEmitter: EventEmitter2);
14
16
  requireMfaEnabledForApp(throwError?: boolean): boolean;
15
17
  private checkIsMfaEnabledForApp;
16
18
  getVerifiedMethods(userId: string): Promise<MFAMethodEnum[]>;
@@ -43,5 +45,7 @@ export declare class MfaService {
43
45
  }>;
44
46
  getAvailableMethods(): MFAMethodEnum[];
45
47
  hasRecoveryCode(userId: string): Promise<boolean>;
48
+ createTrustedDevice(userId: string, userAgent: string, ipAddress: string): Promise<string>;
49
+ validateTrustedDevice(userId: string, token: string): Promise<boolean>;
46
50
  }
47
51
  //# sourceMappingURL=mfa.service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mfa.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/nest-auth/src/lib/auth/services/mfa.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAY,UAAU,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAG1E,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,6CAA6C,CAAC;AAExF,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAK7D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAKtD,qBACa,UAAU;IAMf,OAAO,CAAC,mBAAmB;IAG3B,OAAO,CAAC,cAAc;IAGtB,OAAO,CAAC,aAAa;IAErB,OAAO,CAAC,YAAY;IAZxB,SAAS,EAAE,UAAU,CAAA;gBAIT,mBAAmB,EAAE,UAAU,CAAC,iBAAiB,CAAC,EAGlD,cAAc,EAAE,UAAU,CAAC,YAAY,CAAC,EAGxC,aAAa,EAAE,UAAU,CAAC,WAAW,CAAC,EAEtC,YAAY,EAAE,aAAa;IAKvC,uBAAuB,CAAC,UAAU,GAAE,OAAc;IAUlD,OAAO,CAAC,uBAAuB;IAIzB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAiC5D,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAmC3D,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IA2CpE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAmDpF,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAqBjG,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA6BnF,cAAc,CAAC,MAAM,EAAE,MAAM;;;;;;;;IAmB7B,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgB/C,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAW9C,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/D,SAAS,CAAC,MAAM,EAAE,MAAM;IAexB,UAAU,CAAC,MAAM,EAAE,MAAM;IASzB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMjD,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUrD,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IA6B1E,mBAAmB,IAAI,aAAa,EAAE;IAOhC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAY1D"}
1
+ {"version":3,"file":"mfa.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/nest-auth/src/lib/auth/services/mfa.service.ts"],"names":[],"mappings":"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"}
@@ -19,11 +19,14 @@ const auth_config_service_1 = require("../../core/services/auth-config.service")
19
19
  const event_emitter_1 = require("@nestjs/event-emitter");
20
20
  const auth_constants_2 = require("../../auth.constants");
21
21
  const two_factor_code_sent_event_1 = require("../events/two-factor-code-sent.event");
22
+ const trusted_device_entity_1 = require("../entities/trusted-device.entity");
23
+ const crypto_1 = require("crypto");
22
24
  let MfaService = class MfaService {
23
- constructor(mfaSecretRepository, userRepository, otpRepository, eventEmitter) {
25
+ constructor(mfaSecretRepository, userRepository, otpRepository, trustedDeviceRepository, eventEmitter) {
24
26
  this.mfaSecretRepository = mfaSecretRepository;
25
27
  this.userRepository = userRepository;
26
28
  this.otpRepository = otpRepository;
29
+ this.trustedDeviceRepository = trustedDeviceRepository;
27
30
  this.eventEmitter = eventEmitter;
28
31
  this.mfaConfig = auth_config_service_1.AuthConfigService.getOptions().mfa;
29
32
  }
@@ -104,6 +107,11 @@ let MfaService = class MfaService {
104
107
  if (!expiresAtMs || isNaN(expiresAtMs) || expiresAtMs <= 0) {
105
108
  throw new Error(`Invalid MFA configuration: otpExpiresIn '${this.mfaConfig.otpExpiresIn}' results in invalid duration`);
106
109
  }
110
+ // Invalidate previous MFA OTPs for this user
111
+ await this.otpRepository.delete({
112
+ userId,
113
+ type: otp_interface_1.OTPTypeEnum.MFA
114
+ });
107
115
  const otp = await this.otpRepository.create({
108
116
  userId,
109
117
  type: otp_interface_1.OTPTypeEnum.MFA,
@@ -317,6 +325,35 @@ let MfaService = class MfaService {
317
325
  });
318
326
  return Boolean(user?.mfaRecoveryCode);
319
327
  }
328
+ async createTrustedDevice(userId, userAgent, ipAddress) {
329
+ this.requireMfaEnabledForApp(true);
330
+ const token = (0, crypto_1.randomBytes)(32).toString('hex');
331
+ const duration = this.mfaConfig.trustedDeviceDuration || '30d';
332
+ const expiresAtMs = typeof duration === 'string' ? (0, ms_1.default)(duration) : duration;
333
+ await this.trustedDeviceRepository.save({
334
+ userId,
335
+ token,
336
+ userAgent,
337
+ ipAddress,
338
+ expiresAt: new Date(Date.now() + expiresAtMs),
339
+ });
340
+ return token;
341
+ }
342
+ async validateTrustedDevice(userId, token) {
343
+ if (!token)
344
+ return false;
345
+ const device = await this.trustedDeviceRepository.findOne({
346
+ where: { userId, token },
347
+ });
348
+ if (!device)
349
+ return false;
350
+ if (device.expiresAt < new Date()) {
351
+ await this.trustedDeviceRepository.remove(device);
352
+ return false;
353
+ }
354
+ await this.trustedDeviceRepository.update(device.id, { lastUsedAt: new Date() });
355
+ return true;
356
+ }
320
357
  };
321
358
  exports.MfaService = MfaService;
322
359
  exports.MfaService = MfaService = tslib_1.__decorate([
@@ -324,7 +361,9 @@ exports.MfaService = MfaService = tslib_1.__decorate([
324
361
  tslib_1.__param(0, (0, typeorm_1.InjectRepository)(mfa_secret_entity_1.NestAuthMFASecret)),
325
362
  tslib_1.__param(1, (0, typeorm_1.InjectRepository)(user_entity_1.NestAuthUser)),
326
363
  tslib_1.__param(2, (0, typeorm_1.InjectRepository)(otp_entity_1.NestAuthOTP)),
364
+ tslib_1.__param(3, (0, typeorm_1.InjectRepository)(trusted_device_entity_1.NestAuthTrustedDevice)),
327
365
  tslib_1.__metadata("design:paramtypes", [typeorm_2.Repository,
366
+ typeorm_2.Repository,
328
367
  typeorm_2.Repository,
329
368
  typeorm_2.Repository,
330
369
  event_emitter_1.EventEmitter2])
@@ -17,6 +17,7 @@ export declare const REFRESH_TOKEN_INVALID = "REFRESH_TOKEN_INVALID";
17
17
  export declare const REFRESH_TOKEN_EXPIRED = "REFRESH_TOKEN_EXPIRED";
18
18
  export declare const ACCESS_TOKEN_COOKIE_NAME = "accessToken";
19
19
  export declare const REFRESH_TOKEN_COOKIE_NAME = "refreshToken";
20
+ export declare const NEST_AUTH_TRUST_DEVICE_KEY = "nest_auth_device_trust";
20
21
  export declare const DEFAULT_GUARD_NAME = "web";
21
22
  export declare const NestAuthEvents: {
22
23
  readonly EMAIL_VERIFICATION_REQUESTED: "email.verification.requested";
@@ -1 +1 @@
1
- {"version":3,"file":"auth.constants.d.ts","sourceRoot":"","sources":["../../../../../packages/nest-auth/src/lib/auth.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,kCAAkC,CAAC;AACnE,eAAO,MAAM,gCAAgC,qCAAqC,CAAC;AAInF,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AACvC,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAC7C,eAAO,MAAM,sBAAsB,aAAa,CAAC;AACjD,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAC3C,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAC7C,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAC3C,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAI3C,eAAO,MAAM,6BAA6B,mBAAmB,CAAC;AAC9D,eAAO,MAAM,2BAA2B,iBAAiB,CAAC;AAC1D,eAAO,MAAM,0BAA0B,gBAAgB,CAAC;AACxD,eAAO,MAAM,oCAAoC,0BAA0B,CAAC;AAC5E,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAC3D,eAAO,MAAM,qBAAqB,oBAAoB,CAAC;AAEvD,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAC7D,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAI7D,eAAO,MAAM,wBAAwB,gBAAgB,CAAC;AACtD,eAAO,MAAM,yBAAyB,iBAAiB,CAAC;AAGxD,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAGxC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;CA6BjB,CAAC"}
1
+ {"version":3,"file":"auth.constants.d.ts","sourceRoot":"","sources":["../../../../../packages/nest-auth/src/lib/auth.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,kCAAkC,CAAC;AACnE,eAAO,MAAM,gCAAgC,qCAAqC,CAAC;AAInF,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AACvC,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAC7C,eAAO,MAAM,sBAAsB,aAAa,CAAC;AACjD,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAC3C,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAC7C,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAC3C,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAI3C,eAAO,MAAM,6BAA6B,mBAAmB,CAAC;AAC9D,eAAO,MAAM,2BAA2B,iBAAiB,CAAC;AAC1D,eAAO,MAAM,0BAA0B,gBAAgB,CAAC;AACxD,eAAO,MAAM,oCAAoC,0BAA0B,CAAC;AAC5E,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAC3D,eAAO,MAAM,qBAAqB,oBAAoB,CAAC;AAEvD,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAC7D,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAI7D,eAAO,MAAM,wBAAwB,gBAAgB,CAAC;AACtD,eAAO,MAAM,yBAAyB,iBAAiB,CAAC;AAExD,eAAO,MAAM,0BAA0B,2BAA2B,CAAC;AAGnE,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAGxC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;CA6BjB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NestAuthEvents = exports.DEFAULT_GUARD_NAME = exports.REFRESH_TOKEN_COOKIE_NAME = exports.ACCESS_TOKEN_COOKIE_NAME = exports.REFRESH_TOKEN_EXPIRED = exports.REFRESH_TOKEN_INVALID = exports.USER_NOT_ACTIVE_ERROR = exports.SESSION_NOT_FOUND_ERROR = exports.INVALID_REFRESH_TOKEN_EXCEPTION_CODE = exports.INVALID_MFA_EXCEPTION_CODE = exports.UNAUTHORIZED_EXCEPTION_CODE = exports.USER_NOT_FOUND_EXCEPTION_CODE = exports.PHONE_AUTH_PROVIDER = exports.EMAIL_AUTH_PROVIDER = exports.GITHUB_AUTH_PROVIDER = exports.APPLE_AUTH_PROVIDER = exports.FACEBOOK_AUTH_PROVIDER = exports.GOOGLE_AUTH_PROVIDER = exports.JWT_AUTH_PROVIDER = exports.NEST_AUTH_ASYNC_OPTIONS_PROVIDER = exports.AUTH_MODULE_OPTIONS = void 0;
3
+ exports.NestAuthEvents = exports.DEFAULT_GUARD_NAME = exports.NEST_AUTH_TRUST_DEVICE_KEY = exports.REFRESH_TOKEN_COOKIE_NAME = exports.ACCESS_TOKEN_COOKIE_NAME = exports.REFRESH_TOKEN_EXPIRED = exports.REFRESH_TOKEN_INVALID = exports.USER_NOT_ACTIVE_ERROR = exports.SESSION_NOT_FOUND_ERROR = exports.INVALID_REFRESH_TOKEN_EXCEPTION_CODE = exports.INVALID_MFA_EXCEPTION_CODE = exports.UNAUTHORIZED_EXCEPTION_CODE = exports.USER_NOT_FOUND_EXCEPTION_CODE = exports.PHONE_AUTH_PROVIDER = exports.EMAIL_AUTH_PROVIDER = exports.GITHUB_AUTH_PROVIDER = exports.APPLE_AUTH_PROVIDER = exports.FACEBOOK_AUTH_PROVIDER = exports.GOOGLE_AUTH_PROVIDER = exports.JWT_AUTH_PROVIDER = exports.NEST_AUTH_ASYNC_OPTIONS_PROVIDER = exports.AUTH_MODULE_OPTIONS = void 0;
4
4
  exports.AUTH_MODULE_OPTIONS = 'NEST_AUTH_AUTH_MODULE_OPTIONS';
5
5
  exports.NEST_AUTH_ASYNC_OPTIONS_PROVIDER = 'NEST_AUTH_ASYNC_OPTIONS_PROVIDER';
6
6
  // Provider tokens
@@ -23,6 +23,7 @@ exports.REFRESH_TOKEN_EXPIRED = 'REFRESH_TOKEN_EXPIRED';
23
23
  // Auth Cookie Names
24
24
  exports.ACCESS_TOKEN_COOKIE_NAME = 'accessToken';
25
25
  exports.REFRESH_TOKEN_COOKIE_NAME = 'refreshToken';
26
+ exports.NEST_AUTH_TRUST_DEVICE_KEY = 'nest_auth_device_trust';
26
27
  // Default values
27
28
  exports.DEFAULT_GUARD_NAME = 'web';
28
29
  // Events Const
@@ -17,6 +17,8 @@ export interface MFAOptions {
17
17
  allowUserToggle?: boolean;
18
18
  allowMethodSelection?: boolean;
19
19
  otpExpiresIn?: string | number;
20
+ trustedDeviceDuration?: string | number;
21
+ trustDeviceStorageName?: string;
20
22
  }
21
23
  export declare enum MFAMethodEnum {
22
24
  TOTP = "totp",
@@ -1 +1 @@
1
- {"version":3,"file":"mfa-options.interface.d.ts","sourceRoot":"","sources":["../../../../../../../packages/nest-auth/src/lib/core/interfaces/mfa-options.interface.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,UAAU;IAEvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAGlB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAGnB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAG1B,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,IAAI,CAAC,EAAE;QACH,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAClB,CAAC;IAGF,GAAG,CAAC,EAAE;QACF,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KACpB,CAAC;IAGF,KAAK,CAAC,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC;KACpB,CAAC;IAGF,eAAe,CAAC,EAAE,OAAO,CAAC;IAG1B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAG/B,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAClC;AAED,oBAAY,aAAa;IACrB,IAAI,SAAS;IACb,GAAG,QAAQ;IACX,KAAK,UAAU;CAClB"}
1
+ {"version":3,"file":"mfa-options.interface.d.ts","sourceRoot":"","sources":["../../../../../../../packages/nest-auth/src/lib/core/interfaces/mfa-options.interface.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,UAAU;IAEvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAGlB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAGnB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAG1B,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,IAAI,CAAC,EAAE;QACH,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAClB,CAAC;IAGF,GAAG,CAAC,EAAE;QACF,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KACpB,CAAC;IAGF,KAAK,CAAC,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC;KACpB,CAAC;IAGF,eAAe,CAAC,EAAE,OAAO,CAAC;IAG1B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAG/B,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAG/B,qBAAqB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAGxC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,oBAAY,aAAa;IACrB,IAAI,SAAS;IACb,GAAG,QAAQ;IACX,KAAK,UAAU;CAClB"}