@aranzatech/aranza-auth 0.1.2 → 0.2.0

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/dist/index.js CHANGED
@@ -1,13 +1,36 @@
1
- import { __decorateClass, __decorateParam, AUTH_MODULE_OPTIONS, AUTH_REPOSITORY, AUTH_HOOKS, normalizeIdentifier, resolveRegisterIdentifier, readAccountIdentifier } from './chunk-JLRBMDLH.js';
2
- export { AUTH_HOOKS, AUTH_MODULE_OPTIONS, AUTH_REPOSITORY } from './chunk-JLRBMDLH.js';
1
+ import { __decorateClass, __decorateParam, AUTH_MODULE_OPTIONS, AUTH_REPOSITORY, AUTH_HOOKS, normalizeIdentifier, resolveRegisterIdentifier, readAccountIdentifier } from './chunk-QNEFN5ES.js';
2
+ export { AUTH_HOOKS, AUTH_MODULE_OPTIONS, AUTH_REPOSITORY } from './chunk-QNEFN5ES.js';
3
3
  import { createParamDecorator, UnauthorizedException, Injectable, Inject, Post, Body, HttpCode, HttpStatus, UseGuards, Get, Controller, Module, BadRequestException, NotFoundException } from '@nestjs/common';
4
+ import { ModuleRef } from '@nestjs/core';
4
5
  import { JwtService, JwtModule } from '@nestjs/jwt';
5
6
  import { AuthGuard, PassportStrategy, PassportModule } from '@nestjs/passport';
6
7
  import * as bcrypt2 from 'bcryptjs';
7
8
  import { createHash, randomBytes } from 'crypto';
8
9
  import { Strategy, ExtractJwt } from 'passport-jwt';
9
- import { IsEmail, IsOptional, IsString, Length, IsNotEmpty, Matches } from 'class-validator';
10
+ import { IsString, IsNotEmpty, Length, IsEmail, IsOptional, ValidateIf, Matches } from 'class-validator';
10
11
 
12
+ // src/constants/rate-limit.presets.ts
13
+ var AUTH_RATE_LIMIT_PRESETS = {
14
+ /** General auth routes: 10 requests / minute / IP */
15
+ default: { name: "auth-default", ttl: 6e4, limit: 10 },
16
+ /** Login, register, refresh: 5 requests / minute / IP */
17
+ credentials: { name: "auth-credentials", ttl: 6e4, limit: 5 },
18
+ /** Forgot password: 3 requests / minute / IP */
19
+ passwordReset: { name: "auth-password-reset", ttl: 6e4, limit: 3 }
20
+ };
21
+
22
+ // src/constants/auth-errors.ts
23
+ var AuthErrorCode = {
24
+ INVALID_CREDENTIALS: "Invalid credentials",
25
+ INVALID_REFRESH_TOKEN: "Invalid refresh token",
26
+ REFRESH_TOKEN_REUSE: "REFRESH_TOKEN_REUSE",
27
+ ACCOUNT_DISABLED: "ACCOUNT_DISABLED",
28
+ EMAIL_NOT_VERIFIED: "EMAIL_NOT_VERIFIED",
29
+ TOKEN_INVALID_OR_EXPIRED: "TOKEN_INVALID_OR_EXPIRED",
30
+ ACCOUNT_LOCKED: "ACCOUNT_LOCKED",
31
+ INVALID_CURRENT_PASSWORD: "INVALID_CURRENT_PASSWORD",
32
+ PASSWORD_UNCHANGED: "PASSWORD_UNCHANGED"
33
+ };
11
34
  var CurrentUser = createParamDecorator(
12
35
  (_data, ctx) => {
13
36
  const request = ctx.switchToHttp().getRequest();
@@ -25,6 +48,9 @@ var JwtAuthGuard = class extends AuthGuard("jwt") {
25
48
  JwtAuthGuard = __decorateClass([
26
49
  Injectable()
27
50
  ], JwtAuthGuard);
51
+
52
+ // src/constants/password.constants.ts
53
+ var DUMMY_PASSWORD_HASH = "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy";
28
54
  var DefaultAuthHooks = class {
29
55
  async buildJwtPayload(account) {
30
56
  return {
@@ -39,7 +65,9 @@ var DefaultAuthHooks = class {
39
65
  email: account.email,
40
66
  username: account.username,
41
67
  emailVerified: account.emailVerified,
42
- disabled: account.disabled
68
+ disabled: account.disabled,
69
+ ...account.lastLoginAt != null ? { lastLoginAt: account.lastLoginAt } : {},
70
+ ...account.passwordChangedAt != null ? { passwordChangedAt: account.passwordChangedAt } : {}
43
71
  };
44
72
  }
45
73
  async onBeforeRegister(_input) {
@@ -60,6 +88,14 @@ DefaultAuthHooks = __decorateClass([
60
88
  function isDuplicateKeyError(error) {
61
89
  return !!error && typeof error === "object" && "code" in error && error.code === 11e3;
62
90
  }
91
+ var COMPLEXITY_PATTERN = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/;
92
+ function assertPasswordComplexity(password) {
93
+ if (!COMPLEXITY_PATTERN.test(password)) {
94
+ throw new BadRequestException(
95
+ "Password must contain at least one uppercase letter, one lowercase letter, and one digit"
96
+ );
97
+ }
98
+ }
63
99
  function generateRawToken(byteLength = 32) {
64
100
  return randomBytes(byteLength).toString("hex");
65
101
  }
@@ -71,11 +107,15 @@ function expiresAtFromTtlMs(ttlMs) {
71
107
  }
72
108
  var DEFAULT_EMAIL_VERIFICATION_TTL_MS = 24 * 60 * 60 * 1e3;
73
109
  var DEFAULT_PASSWORD_RESET_TTL_MS = 15 * 60 * 1e3;
110
+ var JWT_ALGORITHM = "HS256";
74
111
  var TokenService = class {
75
112
  constructor(jwtService, options) {
76
113
  this.jwtService = jwtService;
77
114
  this.options = options;
78
115
  }
116
+ get bcryptRounds() {
117
+ return this.options.bcryptRounds ?? 10;
118
+ }
79
119
  async signTokens(payload) {
80
120
  const accessExpiresIn = this.options.expiresIn ?? "1h";
81
121
  const refreshExpiresIn = this.options.refreshExpiresIn ?? "7d";
@@ -84,14 +124,16 @@ var TokenService = class {
84
124
  payload,
85
125
  {
86
126
  secret: this.options.secret,
87
- expiresIn: accessExpiresIn
127
+ expiresIn: accessExpiresIn,
128
+ algorithm: JWT_ALGORITHM
88
129
  }
89
130
  ),
90
131
  this.jwtService.signAsync(
91
132
  payload,
92
133
  {
93
134
  secret: this.options.refreshSecret,
94
- expiresIn: refreshExpiresIn
135
+ expiresIn: refreshExpiresIn,
136
+ algorithm: JWT_ALGORITHM
95
137
  }
96
138
  )
97
139
  ]);
@@ -99,11 +141,12 @@ var TokenService = class {
99
141
  }
100
142
  async verifyRefreshToken(refreshToken) {
101
143
  return this.jwtService.verifyAsync(refreshToken, {
102
- secret: this.options.refreshSecret
144
+ secret: this.options.refreshSecret,
145
+ algorithms: [JWT_ALGORITHM]
103
146
  });
104
147
  }
105
148
  async hashRefreshToken(refreshToken) {
106
- return bcrypt2.hash(refreshToken, 10);
149
+ return bcrypt2.hash(refreshToken, this.bcryptRounds);
107
150
  }
108
151
  async compareRefreshToken(refreshToken, hash3) {
109
152
  return bcrypt2.compare(refreshToken, hash3);
@@ -135,6 +178,15 @@ var AuthService = class {
135
178
  get rotateRefreshToken() {
136
179
  return this.options.features?.refreshTokenRotation !== false;
137
180
  }
181
+ get bcryptRounds() {
182
+ return this.options.bcryptRounds ?? 10;
183
+ }
184
+ get accountLockoutEnabled() {
185
+ return this.options.features?.accountLockout === true;
186
+ }
187
+ get lockoutOptions() {
188
+ return this.options.lockout;
189
+ }
138
190
  resolveLoginIdentifier(dto) {
139
191
  const value = this.identifierField === "email" ? dto.email : dto.username;
140
192
  if (value == null || value.trim() === "") {
@@ -152,7 +204,8 @@ var AuthService = class {
152
204
  await this.hooks.onBeforeRegister?.(input);
153
205
  resolveRegisterIdentifier(input, this.identifierField);
154
206
  this.assertRegisterEmailWhenVerificationEnabled(input);
155
- const passwordHash = await bcrypt2.hash(dto.password, 10);
207
+ this.assertPasswordPolicy(dto.password);
208
+ const passwordHash = await bcrypt2.hash(dto.password, this.bcryptRounds);
156
209
  try {
157
210
  const account = await this.authRepository.create({
158
211
  ...input,
@@ -165,7 +218,7 @@ var AuthService = class {
165
218
  }
166
219
  } catch (error) {
167
220
  if (isDuplicateKeyError(error)) {
168
- throw new UnauthorizedException(`${this.identifierField} already exists`);
221
+ return { registered: true };
169
222
  }
170
223
  throw error;
171
224
  }
@@ -176,17 +229,22 @@ var AuthService = class {
176
229
  const account = await this.authRepository.findByIdentifierWithSecrets(
177
230
  identifier
178
231
  );
179
- if (account?.passwordHash == null) {
180
- throw new UnauthorizedException("Invalid credentials");
232
+ if (account != null) {
233
+ this.assertAccountNotLocked(account);
181
234
  }
182
- this.assertAccountActive(account);
183
- const passwordMatches = await bcrypt2.compare(
184
- dto.password,
185
- account.passwordHash
186
- );
187
- if (!passwordMatches) {
188
- throw new UnauthorizedException("Invalid credentials");
235
+ const passwordHash = account?.passwordHash ?? DUMMY_PASSWORD_HASH;
236
+ const passwordMatches = await bcrypt2.compare(dto.password, passwordHash);
237
+ if (account?.passwordHash == null || !passwordMatches) {
238
+ if (account != null && this.accountLockoutEnabled) {
239
+ await this.authRepository.recordLoginFailure(
240
+ account.id,
241
+ this.lockoutOptions
242
+ );
243
+ }
244
+ throw new UnauthorizedException(AuthErrorCode.INVALID_CREDENTIALS);
189
245
  }
246
+ this.assertAccountActive(account);
247
+ await this.authRepository.recordLoginSuccess(account.id);
190
248
  return this.issueTokens(account);
191
249
  }
192
250
  async refresh(refreshToken) {
@@ -194,19 +252,25 @@ var AuthService = class {
194
252
  try {
195
253
  payload = await this.tokenService.verifyRefreshToken(refreshToken);
196
254
  } catch {
197
- throw new UnauthorizedException("Invalid refresh token");
255
+ throw new UnauthorizedException(AuthErrorCode.INVALID_REFRESH_TOKEN);
198
256
  }
199
257
  const account = await this.authRepository.findByIdWithSecrets(payload.sub);
200
- if (account?.refreshTokenHash == null) {
201
- throw new UnauthorizedException("Invalid refresh token");
258
+ if (account == null) {
259
+ throw new UnauthorizedException(AuthErrorCode.INVALID_REFRESH_TOKEN);
202
260
  }
203
261
  this.assertAccountActive(account);
204
- const tokenMatches = await this.tokenService.compareRefreshToken(
205
- refreshToken,
206
- account.refreshTokenHash
207
- );
208
- if (!tokenMatches) {
209
- throw new UnauthorizedException("Invalid refresh token");
262
+ if (this.rotateRefreshToken) {
263
+ if (account.refreshTokenHash == null) {
264
+ throw new UnauthorizedException(AuthErrorCode.INVALID_REFRESH_TOKEN);
265
+ }
266
+ const tokenMatches = await this.tokenService.compareRefreshToken(
267
+ refreshToken,
268
+ account.refreshTokenHash
269
+ );
270
+ if (!tokenMatches) {
271
+ await this.authRepository.updateRefreshTokenHash(account.id, null);
272
+ throw new UnauthorizedException(AuthErrorCode.REFRESH_TOKEN_REUSE);
273
+ }
210
274
  }
211
275
  return this.issueTokens(account);
212
276
  }
@@ -229,7 +293,7 @@ var AuthService = class {
229
293
  const tokenHash = hashToken(token);
230
294
  const account = await this.authRepository.findByEmailVerificationTokenHash(tokenHash);
231
295
  if (account == null) {
232
- throw new BadRequestException("TOKEN_INVALID_OR_EXPIRED");
296
+ throw new BadRequestException(AuthErrorCode.TOKEN_INVALID_OR_EXPIRED);
233
297
  }
234
298
  await this.authRepository.markEmailVerified(account.id);
235
299
  return { verified: true };
@@ -255,20 +319,55 @@ var AuthService = class {
255
319
  const tokenHash = hashToken(token);
256
320
  const account = await this.authRepository.findByResetTokenHash(tokenHash);
257
321
  if (account == null) {
258
- throw new BadRequestException("TOKEN_INVALID_OR_EXPIRED");
322
+ throw new BadRequestException(AuthErrorCode.TOKEN_INVALID_OR_EXPIRED);
259
323
  }
260
- const passwordHash = await bcrypt2.hash(newPassword, 10);
324
+ this.assertPasswordPolicy(newPassword);
325
+ const passwordHash = await bcrypt2.hash(newPassword, this.bcryptRounds);
261
326
  await this.authRepository.updatePasswordHash(account.id, passwordHash);
262
327
  await this.authRepository.clearResetToken(account.id);
263
328
  await this.authRepository.updateRefreshTokenHash(account.id, null);
264
329
  return { reset: true };
265
330
  }
331
+ async changePassword(authId, currentPassword, newPassword) {
332
+ const account = await this.authRepository.findByIdWithSecrets(authId);
333
+ if (account?.passwordHash == null) {
334
+ throw new UnauthorizedException(AuthErrorCode.INVALID_CURRENT_PASSWORD);
335
+ }
336
+ const currentMatches = await bcrypt2.compare(
337
+ currentPassword,
338
+ account.passwordHash
339
+ );
340
+ if (!currentMatches) {
341
+ throw new UnauthorizedException(AuthErrorCode.INVALID_CURRENT_PASSWORD);
342
+ }
343
+ if (currentPassword === newPassword) {
344
+ throw new BadRequestException(AuthErrorCode.PASSWORD_UNCHANGED);
345
+ }
346
+ this.assertPasswordPolicy(newPassword);
347
+ const passwordHash = await bcrypt2.hash(newPassword, this.bcryptRounds);
348
+ await this.authRepository.updatePasswordHash(account.id, passwordHash);
349
+ await this.authRepository.updateRefreshTokenHash(account.id, null);
350
+ return { changed: true };
351
+ }
352
+ assertAccountNotLocked(account) {
353
+ if (!this.accountLockoutEnabled || account.lockedUntil == null) {
354
+ return;
355
+ }
356
+ if (account.lockedUntil > /* @__PURE__ */ new Date()) {
357
+ throw new UnauthorizedException(AuthErrorCode.ACCOUNT_LOCKED);
358
+ }
359
+ }
266
360
  assertAccountActive(account) {
267
361
  if (account.disabled) {
268
- throw new UnauthorizedException("ACCOUNT_DISABLED");
362
+ throw new UnauthorizedException(AuthErrorCode.ACCOUNT_DISABLED);
269
363
  }
270
364
  if (this.emailVerificationEnabled && !account.emailVerified) {
271
- throw new UnauthorizedException("EMAIL_NOT_VERIFIED");
365
+ throw new UnauthorizedException(AuthErrorCode.EMAIL_NOT_VERIFIED);
366
+ }
367
+ }
368
+ assertPasswordPolicy(password) {
369
+ if (this.options.passwordComplexity === true) {
370
+ assertPasswordComplexity(password);
272
371
  }
273
372
  }
274
373
  async issueTokens(account) {
@@ -384,6 +483,13 @@ var AuthController = class {
384
483
  resetPassword(dto) {
385
484
  return this.authService.resetPassword(dto.token, dto.newPassword);
386
485
  }
486
+ changePassword(user, dto) {
487
+ return this.authService.changePassword(
488
+ user.sub,
489
+ dto.currentPassword,
490
+ dto.newPassword
491
+ );
492
+ }
387
493
  };
388
494
  __decorateClass([
389
495
  Post("register"),
@@ -424,17 +530,39 @@ __decorateClass([
424
530
  HttpCode(HttpStatus.OK),
425
531
  __decorateParam(0, Body())
426
532
  ], AuthController.prototype, "resetPassword", 1);
533
+ __decorateClass([
534
+ Post("change-password"),
535
+ UseGuards(JwtAuthGuard),
536
+ HttpCode(HttpStatus.OK),
537
+ __decorateParam(0, CurrentUser()),
538
+ __decorateParam(1, Body())
539
+ ], AuthController.prototype, "changePassword", 1);
427
540
  AuthController = __decorateClass([
428
541
  Controller("auth"),
429
542
  __decorateParam(0, Inject(AuthService))
430
543
  ], AuthController);
544
+
545
+ // src/controllers/auth.controller.factory.ts
546
+ function createAuthController(routePrefix = "auth") {
547
+ let ConfiguredAuthController = class extends AuthController {
548
+ };
549
+ ConfiguredAuthController = __decorateClass([
550
+ Controller(routePrefix)
551
+ ], ConfiguredAuthController);
552
+ Object.defineProperty(ConfiguredAuthController, "name", {
553
+ value: `AuthController_${routePrefix.replace(/\W+/g, "_")}`
554
+ });
555
+ return ConfiguredAuthController;
556
+ }
431
557
  var JwtStrategy = class extends PassportStrategy(Strategy) {
432
558
  constructor(options, authRepository) {
433
559
  super({
434
560
  jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
435
561
  ignoreExpiration: false,
436
- secretOrKey: options.secret
562
+ secretOrKey: options.secret,
563
+ algorithms: ["HS256"]
437
564
  });
565
+ this.options = options;
438
566
  this.authRepository = authRepository;
439
567
  }
440
568
  async validate(payload) {
@@ -442,6 +570,9 @@ var JwtStrategy = class extends PassportStrategy(Strategy) {
442
570
  if (account == null || account.disabled) {
443
571
  throw new UnauthorizedException("Account not found or inactive");
444
572
  }
573
+ if (this.options.features?.emailVerification === true && !account.emailVerified) {
574
+ throw new UnauthorizedException(AuthErrorCode.EMAIL_NOT_VERIFIED);
575
+ }
445
576
  return payload;
446
577
  }
447
578
  };
@@ -451,27 +582,82 @@ JwtStrategy = __decorateClass([
451
582
  __decorateParam(1, Inject(AUTH_REPOSITORY))
452
583
  ], JwtStrategy);
453
584
 
585
+ // src/utils/hooks-provider.util.ts
586
+ function createHooksProvider(options) {
587
+ if (options.hooksProvider != null) {
588
+ return options.hooksProvider;
589
+ }
590
+ const HooksClass = options.hooks ?? DefaultAuthHooks;
591
+ return {
592
+ provide: AUTH_HOOKS,
593
+ useClass: HooksClass
594
+ };
595
+ }
596
+
597
+ // src/utils/validate-auth-config.util.ts
598
+ var MIN_SECRET_LENGTH = 32;
599
+ var MIN_BCRYPT_ROUNDS = 10;
600
+ var MAX_BCRYPT_ROUNDS = 14;
601
+ function validateAuthModuleOptions(options) {
602
+ if (options.secret.length < MIN_SECRET_LENGTH) {
603
+ throw new Error(
604
+ `AuthModule: secret must be at least ${MIN_SECRET_LENGTH} characters`
605
+ );
606
+ }
607
+ if (options.refreshSecret.length < MIN_SECRET_LENGTH) {
608
+ throw new Error(
609
+ `AuthModule: refreshSecret must be at least ${MIN_SECRET_LENGTH} characters`
610
+ );
611
+ }
612
+ if (options.secret === options.refreshSecret) {
613
+ throw new Error(
614
+ "AuthModule: secret and refreshSecret must be different"
615
+ );
616
+ }
617
+ const rounds = options.bcryptRounds ?? MIN_BCRYPT_ROUNDS;
618
+ if (rounds < MIN_BCRYPT_ROUNDS || rounds > MAX_BCRYPT_ROUNDS) {
619
+ throw new Error(
620
+ `AuthModule: bcryptRounds must be between ${MIN_BCRYPT_ROUNDS} and ${MAX_BCRYPT_ROUNDS}`
621
+ );
622
+ }
623
+ if (options.emailVerificationTokenTtlMs != null && options.emailVerificationTokenTtlMs < 6e4) {
624
+ throw new Error(
625
+ "AuthModule: emailVerificationTokenTtlMs must be at least 60000 (1 minute)"
626
+ );
627
+ }
628
+ if (options.passwordResetTokenTtlMs != null && options.passwordResetTokenTtlMs < 6e4) {
629
+ throw new Error(
630
+ "AuthModule: passwordResetTokenTtlMs must be at least 60000 (1 minute)"
631
+ );
632
+ }
633
+ }
634
+
454
635
  // src/auth.module.ts
455
- function createAuthProviders(options) {
636
+ function createCoreProviders(options) {
637
+ validateAuthModuleOptions(options);
456
638
  return [
457
639
  {
458
640
  provide: AUTH_MODULE_OPTIONS,
459
641
  useValue: options
460
642
  },
461
- {
462
- provide: AUTH_HOOKS,
463
- inject: [AUTH_MODULE_OPTIONS],
464
- useFactory: (opts) => {
465
- const HooksClass = opts.hooks ?? DefaultAuthHooks;
466
- return new HooksClass();
467
- }
468
- },
643
+ createHooksProvider(options),
469
644
  AuthService,
470
645
  TokenService,
471
646
  JwtStrategy,
472
647
  JwtAuthGuard
473
648
  ];
474
649
  }
650
+ function createAsyncHooksProvider(options) {
651
+ if (options.hooksProvider != null) {
652
+ return options.hooksProvider;
653
+ }
654
+ const HooksClass = options.hooks ?? DefaultAuthHooks;
655
+ return {
656
+ provide: AUTH_HOOKS,
657
+ inject: [ModuleRef],
658
+ useFactory: (moduleRef) => moduleRef.create(HooksClass)
659
+ };
660
+ }
475
661
  function createAuthImports() {
476
662
  return [
477
663
  PassportModule.register({ defaultStrategy: "jwt" }),
@@ -479,7 +665,10 @@ function createAuthImports() {
479
665
  inject: [AUTH_MODULE_OPTIONS],
480
666
  useFactory: (opts) => ({
481
667
  secret: opts.secret,
482
- signOptions: { expiresIn: opts.expiresIn ?? "1h" }
668
+ signOptions: {
669
+ expiresIn: opts.expiresIn ?? "1h",
670
+ algorithm: "HS256"
671
+ }
483
672
  })
484
673
  })
485
674
  ];
@@ -491,14 +680,33 @@ function mergeImports(userImports) {
491
680
  }
492
681
  return merged;
493
682
  }
683
+ function createAsyncProviders(options) {
684
+ return [
685
+ {
686
+ provide: AUTH_MODULE_OPTIONS,
687
+ inject: options.inject ?? [],
688
+ useFactory: async (...args) => {
689
+ const config = await options.useFactory(...args);
690
+ validateAuthModuleOptions(config);
691
+ return config;
692
+ }
693
+ },
694
+ createAsyncHooksProvider(options),
695
+ AuthService,
696
+ TokenService,
697
+ JwtStrategy,
698
+ JwtAuthGuard
699
+ ];
700
+ }
494
701
  var AuthModule = class {
495
702
  static forRoot(options) {
703
+ const routePrefix = options.routePrefix ?? "auth";
496
704
  return {
497
705
  module: AuthModule,
498
706
  global: true,
499
707
  imports: createAuthImports(),
500
- controllers: [AuthController],
501
- providers: createAuthProviders(options),
708
+ controllers: [createAuthController(routePrefix)],
709
+ providers: createCoreProviders(options),
502
710
  exports: [
503
711
  AUTH_MODULE_OPTIONS,
504
712
  AUTH_HOOKS,
@@ -511,30 +719,13 @@ var AuthModule = class {
511
719
  };
512
720
  }
513
721
  static forRootAsync(options) {
722
+ const routePrefix = options.routePrefix ?? "auth";
514
723
  return {
515
724
  module: AuthModule,
516
725
  global: true,
517
726
  imports: mergeImports(options.imports),
518
- controllers: [AuthController],
519
- providers: [
520
- {
521
- provide: AUTH_MODULE_OPTIONS,
522
- inject: options.inject ?? [],
523
- useFactory: options.useFactory
524
- },
525
- {
526
- provide: AUTH_HOOKS,
527
- inject: [AUTH_MODULE_OPTIONS],
528
- useFactory: (opts) => {
529
- const HooksClass = opts.hooks ?? DefaultAuthHooks;
530
- return new HooksClass();
531
- }
532
- },
533
- AuthService,
534
- TokenService,
535
- JwtStrategy,
536
- JwtAuthGuard
537
- ],
727
+ controllers: [createAuthController(routePrefix)],
728
+ providers: createAsyncProviders(options),
538
729
  exports: [
539
730
  AUTH_MODULE_OPTIONS,
540
731
  AUTH_HOOKS,
@@ -554,6 +745,17 @@ AuthModule = __decorateClass([
554
745
  // src/dto/auth-tokens.dto.ts
555
746
  var AuthTokensDto = class {
556
747
  };
748
+ var ChangePasswordDto = class {
749
+ };
750
+ __decorateClass([
751
+ IsString(),
752
+ IsNotEmpty()
753
+ ], ChangePasswordDto.prototype, "currentPassword", 2);
754
+ __decorateClass([
755
+ IsString(),
756
+ IsNotEmpty(),
757
+ Length(8, 128)
758
+ ], ChangePasswordDto.prototype, "newPassword", 2);
557
759
  var ForgotPasswordDto = class {
558
760
  };
559
761
  __decorateClass([
@@ -563,7 +765,8 @@ var LoginDto = class {
563
765
  };
564
766
  __decorateClass([
565
767
  IsOptional(),
566
- IsString(),
768
+ ValidateIf((dto) => dto.email != null && dto.email.trim() !== ""),
769
+ IsEmail(),
567
770
  Length(3, 255)
568
771
  ], LoginDto.prototype, "email", 2);
569
772
  __decorateClass([
@@ -590,7 +793,8 @@ var RegisterDto = class {
590
793
  };
591
794
  __decorateClass([
592
795
  IsOptional(),
593
- IsString(),
796
+ ValidateIf((dto) => dto.email != null && dto.email.trim() !== ""),
797
+ IsEmail(),
594
798
  Length(3, 255)
595
799
  ], RegisterDto.prototype, "email", 2);
596
800
  __decorateClass([
@@ -622,6 +826,6 @@ __decorateClass([
622
826
  IsNotEmpty()
623
827
  ], VerifyEmailDto.prototype, "token", 2);
624
828
 
625
- export { AuthModule, AuthService, AuthTokensDto, CurrentUser, DefaultAuthHooks, ForgotPasswordDto, JwtAuthGuard, LoginDto, RefreshTokenDto, RegisterAckDto, RegisterDto, ResetPasswordDto, TokenService, VerifyEmailDto };
829
+ export { AUTH_RATE_LIMIT_PRESETS, AuthErrorCode, AuthModule, AuthService, AuthTokensDto, ChangePasswordDto, CurrentUser, DefaultAuthHooks, ForgotPasswordDto, JwtAuthGuard, LoginDto, RefreshTokenDto, RegisterAckDto, RegisterDto, ResetPasswordDto, TokenService, VerifyEmailDto };
626
830
  //# sourceMappingURL=index.js.map
627
831
  //# sourceMappingURL=index.js.map