@aranzatech/aranza-auth 0.2.1 → 0.2.2

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,14 @@
1
1
  import { __decorateClass, __decorateParam, AUTH_MODULE_OPTIONS, AUTH_REPOSITORY, AUTH_HOOKS, normalizeIdentifier, resolveRegisterIdentifier, readAccountIdentifier } from './chunk-QNEFN5ES.js';
2
2
  export { AUTH_HOOKS, AUTH_MODULE_OPTIONS, AUTH_REPOSITORY } from './chunk-QNEFN5ES.js';
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';
3
+ import { ApiProperty, ApiPropertyOptional, ApiOperation, ApiResponse, ApiBearerAuth, ApiTags, ApiUnauthorizedResponse, DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
4
+ import { IsString, IsNotEmpty, Length, IsEmail, IsOptional, ValidateIf, Matches } from 'class-validator';
5
+ import { createParamDecorator, UnauthorizedException, Injectable, Inject, Post, Body, HttpCode, HttpStatus, UseGuards, Get, Module, BadRequestException, NotFoundException, applyDecorators, Controller } from '@nestjs/common';
5
6
  import { JwtService, JwtModule } from '@nestjs/jwt';
6
7
  import { AuthGuard, PassportStrategy, PassportModule } from '@nestjs/passport';
7
- import * as bcrypt2 from 'bcryptjs';
8
- import { createHash, randomBytes } from 'crypto';
8
+ import * as bcrypt from 'bcryptjs';
9
+ import { createHmac, timingSafeEqual, createHash, randomBytes, randomUUID } from 'crypto';
9
10
  import { Strategy, ExtractJwt } from 'passport-jwt';
10
- import { IsString, IsNotEmpty, Length, IsEmail, IsOptional, ValidateIf, Matches } from 'class-validator';
11
+ import { ModuleRef } from '@nestjs/core';
11
12
 
12
13
  // src/constants/rate-limit.presets.ts
13
14
  var AUTH_RATE_LIMIT_PRESETS = {
@@ -19,18 +20,258 @@ var AUTH_RATE_LIMIT_PRESETS = {
19
20
  passwordReset: { name: "auth-password-reset", ttl: 6e4, limit: 3 }
20
21
  };
21
22
 
23
+ // src/constants/rate-limit.routes.ts
24
+ var AUTH_RATE_LIMIT_ROUTES = {
25
+ login: AUTH_RATE_LIMIT_PRESETS.credentials,
26
+ register: AUTH_RATE_LIMIT_PRESETS.credentials,
27
+ refresh: AUTH_RATE_LIMIT_PRESETS.credentials,
28
+ "forgot-password": AUTH_RATE_LIMIT_PRESETS.passwordReset,
29
+ "reset-password": AUTH_RATE_LIMIT_PRESETS.passwordReset,
30
+ "resend-verification": AUTH_RATE_LIMIT_PRESETS.passwordReset,
31
+ default: AUTH_RATE_LIMIT_PRESETS.default
32
+ };
33
+
22
34
  // src/constants/auth-errors.ts
23
35
  var AuthErrorCode = {
24
- INVALID_CREDENTIALS: "Invalid credentials",
25
- INVALID_REFRESH_TOKEN: "Invalid refresh token",
36
+ INVALID_CREDENTIALS: "INVALID_CREDENTIALS",
37
+ INVALID_REFRESH_TOKEN: "INVALID_REFRESH_TOKEN",
26
38
  REFRESH_TOKEN_REUSE: "REFRESH_TOKEN_REUSE",
27
39
  ACCOUNT_DISABLED: "ACCOUNT_DISABLED",
40
+ ACCOUNT_NOT_FOUND: "ACCOUNT_NOT_FOUND",
28
41
  EMAIL_NOT_VERIFIED: "EMAIL_NOT_VERIFIED",
29
42
  TOKEN_INVALID_OR_EXPIRED: "TOKEN_INVALID_OR_EXPIRED",
30
43
  ACCOUNT_LOCKED: "ACCOUNT_LOCKED",
31
44
  INVALID_CURRENT_PASSWORD: "INVALID_CURRENT_PASSWORD",
32
- PASSWORD_UNCHANGED: "PASSWORD_UNCHANGED"
45
+ PASSWORD_UNCHANGED: "PASSWORD_UNCHANGED",
46
+ PASSWORD_CHANGED: "PASSWORD_CHANGED",
47
+ /** Missing or invalid Bearer token on a protected route. */
48
+ UNAUTHORIZED: "UNAUTHORIZED"
49
+ };
50
+ var AuthTokensDto = class {
51
+ };
52
+ __decorateClass([
53
+ ApiProperty({ example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." })
54
+ ], AuthTokensDto.prototype, "accessToken", 2);
55
+ __decorateClass([
56
+ ApiProperty({ example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." })
57
+ ], AuthTokensDto.prototype, "refreshToken", 2);
58
+ var ChangePasswordDto = class {
59
+ };
60
+ __decorateClass([
61
+ ApiProperty({ example: "CurrentPassword1", minLength: 1, maxLength: 128 }),
62
+ IsString(),
63
+ IsNotEmpty(),
64
+ Length(1, 128)
65
+ ], ChangePasswordDto.prototype, "currentPassword", 2);
66
+ __decorateClass([
67
+ ApiProperty({ example: "NewPassword1", minLength: 8, maxLength: 128 }),
68
+ IsString(),
69
+ IsNotEmpty(),
70
+ Length(8, 128)
71
+ ], ChangePasswordDto.prototype, "newPassword", 2);
72
+ var ForgotPasswordDto = class {
73
+ };
74
+ __decorateClass([
75
+ ApiProperty({ example: "user@example.com" }),
76
+ IsEmail()
77
+ ], ForgotPasswordDto.prototype, "email", 2);
78
+ var LoginDto = class {
79
+ };
80
+ __decorateClass([
81
+ ApiPropertyOptional({ example: "user@example.com" }),
82
+ IsOptional(),
83
+ ValidateIf((dto) => dto.email != null && dto.email.trim() !== ""),
84
+ IsEmail(),
85
+ Length(3, 255)
86
+ ], LoginDto.prototype, "email", 2);
87
+ __decorateClass([
88
+ ApiPropertyOptional({ example: "johndoe" }),
89
+ IsOptional(),
90
+ IsString(),
91
+ Length(3, 50)
92
+ ], LoginDto.prototype, "username", 2);
93
+ __decorateClass([
94
+ ApiProperty({ example: "Password1", minLength: 8, maxLength: 128 }),
95
+ IsString(),
96
+ IsNotEmpty(),
97
+ Length(8, 128)
98
+ ], LoginDto.prototype, "password", 2);
99
+ var MeResponseDto = class {
100
+ };
101
+ __decorateClass([
102
+ ApiProperty({ example: "507f1f77bcf86cd799439011" })
103
+ ], MeResponseDto.prototype, "id", 2);
104
+ __decorateClass([
105
+ ApiPropertyOptional({ example: "user@example.com" })
106
+ ], MeResponseDto.prototype, "email", 2);
107
+ __decorateClass([
108
+ ApiPropertyOptional({ example: "johndoe" })
109
+ ], MeResponseDto.prototype, "username", 2);
110
+ __decorateClass([
111
+ ApiProperty({ example: true })
112
+ ], MeResponseDto.prototype, "emailVerified", 2);
113
+ __decorateClass([
114
+ ApiProperty({ example: false })
115
+ ], MeResponseDto.prototype, "disabled", 2);
116
+ __decorateClass([
117
+ ApiPropertyOptional({ type: String, format: "date-time" })
118
+ ], MeResponseDto.prototype, "lastLoginAt", 2);
119
+ __decorateClass([
120
+ ApiPropertyOptional({ type: String, format: "date-time" })
121
+ ], MeResponseDto.prototype, "passwordChangedAt", 2);
122
+ var RefreshTokenDto = class {
123
+ };
124
+ __decorateClass([
125
+ ApiProperty({ example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }),
126
+ IsString(),
127
+ IsNotEmpty()
128
+ ], RefreshTokenDto.prototype, "refreshToken", 2);
129
+ var RegisterAckDto = class {
130
+ };
131
+ __decorateClass([
132
+ ApiProperty({ example: true, enum: [true] })
133
+ ], RegisterAckDto.prototype, "registered", 2);
134
+ var RegisterDto = class {
135
+ };
136
+ __decorateClass([
137
+ ApiPropertyOptional({ example: "user@example.com" }),
138
+ IsOptional(),
139
+ ValidateIf((dto) => dto.email != null && dto.email.trim() !== ""),
140
+ IsEmail(),
141
+ Length(3, 255)
142
+ ], RegisterDto.prototype, "email", 2);
143
+ __decorateClass([
144
+ ApiPropertyOptional({ example: "johndoe" }),
145
+ IsOptional(),
146
+ IsString(),
147
+ Length(3, 50),
148
+ Matches(/^[a-zA-Z0-9._-]+$/)
149
+ ], RegisterDto.prototype, "username", 2);
150
+ __decorateClass([
151
+ ApiProperty({ example: "Password1", minLength: 8, maxLength: 128 }),
152
+ IsString(),
153
+ IsNotEmpty(),
154
+ Length(8, 128)
155
+ ], RegisterDto.prototype, "password", 2);
156
+ var ResendVerificationDto = class {
157
+ };
158
+ __decorateClass([
159
+ ApiProperty({ example: "user@example.com" }),
160
+ IsEmail()
161
+ ], ResendVerificationDto.prototype, "email", 2);
162
+ var ResetPasswordDto = class {
163
+ };
164
+ __decorateClass([
165
+ ApiProperty({ example: "reset-token-from-email" }),
166
+ IsString(),
167
+ IsNotEmpty()
168
+ ], ResetPasswordDto.prototype, "token", 2);
169
+ __decorateClass([
170
+ ApiProperty({ example: "NewPassword1", minLength: 8, maxLength: 128 }),
171
+ IsString(),
172
+ IsNotEmpty(),
173
+ Length(8, 128)
174
+ ], ResetPasswordDto.prototype, "newPassword", 2);
175
+ var VerifyEmailDto = class {
33
176
  };
177
+ __decorateClass([
178
+ ApiProperty({ example: "verification-token-from-email" }),
179
+ IsString(),
180
+ IsNotEmpty()
181
+ ], VerifyEmailDto.prototype, "token", 2);
182
+
183
+ // src/swagger/setup-swagger.util.ts
184
+ var AUTH_SWAGGER_MODELS = [
185
+ AuthTokensDto,
186
+ ChangePasswordDto,
187
+ ForgotPasswordDto,
188
+ LoginDto,
189
+ MeResponseDto,
190
+ RefreshTokenDto,
191
+ RegisterAckDto,
192
+ RegisterDto,
193
+ ResendVerificationDto,
194
+ ResetPasswordDto,
195
+ VerifyEmailDto
196
+ ];
197
+ function describeEnabledFeatures(features) {
198
+ const lines = [];
199
+ if (features.emailVerification === true) {
200
+ lines.push("- Email verification (`POST /auth/verify-email`, `POST /auth/resend-verification`)");
201
+ }
202
+ if (features.passwordReset === true) {
203
+ lines.push("- Password reset (`POST /auth/forgot-password`, `POST /auth/reset-password`)");
204
+ }
205
+ if (features.refreshTokenRotation === false) {
206
+ lines.push("- Refresh token rotation **disabled** (stateless refresh until JWT expiry)");
207
+ }
208
+ if (features.accountLockout === true) {
209
+ lines.push("- Account lockout after failed logins");
210
+ }
211
+ if (lines.length === 0) {
212
+ return "";
213
+ }
214
+ return `
215
+
216
+ ## Auth features enabled
217
+ ${lines.join("\n")}`;
218
+ }
219
+ function setupAuthSwagger(app, options = {}) {
220
+ const nestApp = app;
221
+ const baseDescription = options.description ?? "REST API with JWT authentication via @aranzatech/aranza-auth";
222
+ const config = new DocumentBuilder().setTitle(options.title ?? "API").setDescription(
223
+ `${baseDescription}${describeEnabledFeatures(options.features ?? {})}`
224
+ ).setVersion(options.version ?? "1.0").addBearerAuth(
225
+ {
226
+ type: "http",
227
+ scheme: "bearer",
228
+ bearerFormat: "JWT",
229
+ description: "Access token from POST /auth/login"
230
+ },
231
+ "access-token"
232
+ ).build();
233
+ const document = SwaggerModule.createDocument(nestApp, config, {
234
+ extraModels: [...AUTH_SWAGGER_MODELS]
235
+ });
236
+ if (options.exportPath != null) {
237
+ void import('fs/promises').then(
238
+ ({ writeFile }) => writeFile(options.exportPath, JSON.stringify(document, null, 2), "utf8")
239
+ );
240
+ }
241
+ SwaggerModule.setup(options.path ?? "api", nestApp, document);
242
+ }
243
+
244
+ // src/utils/refresh-token-cookie.util.ts
245
+ var DEFAULT_COOKIE_NAME = "refresh_token";
246
+ var DEFAULT_MAX_AGE_SECONDS = 7 * 24 * 60 * 60;
247
+ function resolveCookieOptions(options = {}) {
248
+ return {
249
+ name: options.name ?? DEFAULT_COOKIE_NAME,
250
+ path: options.path ?? "/auth/refresh",
251
+ secure: options.secure ?? true,
252
+ sameSite: options.sameSite ?? "strict",
253
+ maxAgeSeconds: options.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS,
254
+ httpOnly: options.httpOnly ?? true
255
+ };
256
+ }
257
+ function formatCookieAttributes(options) {
258
+ const parts = [
259
+ `Path=${options.path}`,
260
+ `Max-Age=${options.maxAgeSeconds}`,
261
+ `SameSite=${options.sameSite}`
262
+ ];
263
+ if (options.secure) parts.push("Secure");
264
+ if (options.httpOnly) parts.push("HttpOnly");
265
+ return parts.join("; ");
266
+ }
267
+ function buildRefreshTokenCookie(refreshToken, options = {}) {
268
+ const resolved = resolveCookieOptions(options);
269
+ return `${resolved.name}=${encodeURIComponent(refreshToken)}; ${formatCookieAttributes(resolved)}`;
270
+ }
271
+ function buildClearRefreshTokenCookie(options = {}) {
272
+ const resolved = resolveCookieOptions(options);
273
+ return `${resolved.name}=; Path=${resolved.path}; Max-Age=0; HttpOnly`;
274
+ }
34
275
  var CurrentUser = createParamDecorator(
35
276
  (_data, ctx) => {
36
277
  const request = ctx.switchToHttp().getRequest();
@@ -39,8 +280,11 @@ var CurrentUser = createParamDecorator(
39
280
  );
40
281
  var JwtAuthGuard = class extends AuthGuard("jwt") {
41
282
  handleRequest(err, user, _info) {
42
- if (err != null || !user) {
43
- throw err ?? new UnauthorizedException();
283
+ if (err != null) {
284
+ throw err;
285
+ }
286
+ if (!user) {
287
+ throw new UnauthorizedException(AuthErrorCode.UNAUTHORIZED);
44
288
  }
45
289
  return user;
46
290
  }
@@ -54,7 +298,6 @@ var DUMMY_PASSWORD_HASH = "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZd
54
298
  var DefaultAuthHooks = class {
55
299
  async buildJwtPayload(account) {
56
300
  return {
57
- sub: account.id,
58
301
  ...account.email != null ? { email: account.email } : {},
59
302
  ...account.username != null ? { username: account.username } : {}
60
303
  };
@@ -107,49 +350,133 @@ function expiresAtFromTtlMs(ttlMs) {
107
350
  }
108
351
  var DEFAULT_EMAIL_VERIFICATION_TTL_MS = 24 * 60 * 60 * 1e3;
109
352
  var DEFAULT_PASSWORD_RESET_TTL_MS = 15 * 60 * 1e3;
353
+ var JWT_TOKEN_TYPE = {
354
+ ACCESS: "access",
355
+ REFRESH: "refresh"
356
+ };
357
+ function issuerAudienceClaims(options) {
358
+ return {
359
+ ...options.jwtIssuer != null ? { iss: options.jwtIssuer } : {},
360
+ ...options.jwtAudience != null ? { aud: options.jwtAudience } : {}
361
+ };
362
+ }
363
+ function buildAccessClaims(hookClaims, sub, pwdAt, options) {
364
+ return {
365
+ ...hookClaims,
366
+ sub,
367
+ typ: JWT_TOKEN_TYPE.ACCESS,
368
+ ...pwdAt != null ? { pwdAt } : {},
369
+ ...issuerAudienceClaims(options)
370
+ };
371
+ }
372
+ function buildRefreshClaims(sub, pwdAt, options) {
373
+ return {
374
+ sub,
375
+ typ: JWT_TOKEN_TYPE.REFRESH,
376
+ jti: randomUUID(),
377
+ ...pwdAt != null ? { pwdAt } : {},
378
+ ...issuerAudienceClaims(options)
379
+ };
380
+ }
381
+ function assertIssuerAudience(payload, options, errorCode) {
382
+ if (options.jwtIssuer != null && payload.iss != null && payload.iss !== options.jwtIssuer) {
383
+ throw new UnauthorizedException(errorCode);
384
+ }
385
+ if (options.jwtAudience != null && payload.aud != null && payload.aud !== options.jwtAudience) {
386
+ throw new UnauthorizedException(errorCode);
387
+ }
388
+ }
389
+ function assertAccessTokenClaims(payload, options) {
390
+ if (payload.typ != null && payload.typ !== JWT_TOKEN_TYPE.ACCESS) {
391
+ throw new UnauthorizedException(AuthErrorCode.INVALID_CREDENTIALS);
392
+ }
393
+ assertIssuerAudience(payload, options, AuthErrorCode.INVALID_CREDENTIALS);
394
+ }
395
+ function assertRefreshTokenClaims(payload, options) {
396
+ if (payload.typ !== JWT_TOKEN_TYPE.REFRESH) {
397
+ throw new UnauthorizedException(AuthErrorCode.INVALID_REFRESH_TOKEN);
398
+ }
399
+ if (typeof payload.sub !== "string" || payload.sub.length === 0) {
400
+ throw new UnauthorizedException(AuthErrorCode.INVALID_REFRESH_TOKEN);
401
+ }
402
+ assertIssuerAudience(payload, options, AuthErrorCode.INVALID_REFRESH_TOKEN);
403
+ return payload;
404
+ }
405
+ var HMAC_ALGORITHM = "sha256";
406
+ function hashRefreshTokenValue(refreshToken, secret) {
407
+ return createHmac(HMAC_ALGORITHM, secret).update(refreshToken).digest("hex");
408
+ }
409
+ function compareRefreshTokenValue(refreshToken, storedHash, secret) {
410
+ const computed = hashRefreshTokenValue(refreshToken, secret);
411
+ try {
412
+ const a = Buffer.from(computed, "hex");
413
+ const b = Buffer.from(storedHash, "hex");
414
+ if (a.length !== b.length) return false;
415
+ return timingSafeEqual(a, b);
416
+ } catch {
417
+ return false;
418
+ }
419
+ }
420
+
421
+ // src/services/token.service.ts
110
422
  var JWT_ALGORITHM = "HS256";
111
423
  var TokenService = class {
112
424
  constructor(jwtService, options) {
113
425
  this.jwtService = jwtService;
114
426
  this.options = options;
115
427
  }
116
- get bcryptRounds() {
117
- return this.options.bcryptRounds ?? 10;
428
+ signOptions(secret, expiresIn) {
429
+ return {
430
+ secret,
431
+ expiresIn,
432
+ algorithm: JWT_ALGORITHM,
433
+ ...this.options.jwtIssuer != null ? { issuer: this.options.jwtIssuer } : {},
434
+ ...this.options.jwtAudience != null ? { audience: this.options.jwtAudience } : {}
435
+ };
118
436
  }
119
- async signTokens(payload) {
437
+ async signTokens(accessClaims, refreshClaims) {
120
438
  const accessExpiresIn = this.options.expiresIn ?? "1h";
121
439
  const refreshExpiresIn = this.options.refreshExpiresIn ?? "7d";
122
440
  const [accessToken, refreshToken] = await Promise.all([
123
441
  this.jwtService.signAsync(
124
- payload,
125
- {
126
- secret: this.options.secret,
127
- expiresIn: accessExpiresIn,
128
- algorithm: JWT_ALGORITHM
129
- }
442
+ accessClaims,
443
+ this.signOptions(this.options.secret, accessExpiresIn)
130
444
  ),
131
445
  this.jwtService.signAsync(
132
- payload,
133
- {
134
- secret: this.options.refreshSecret,
135
- expiresIn: refreshExpiresIn,
136
- algorithm: JWT_ALGORITHM
137
- }
446
+ refreshClaims,
447
+ this.signOptions(this.options.refreshSecret, refreshExpiresIn)
138
448
  )
139
449
  ]);
140
450
  return { accessToken, refreshToken };
141
451
  }
142
452
  async verifyRefreshToken(refreshToken) {
143
- return this.jwtService.verifyAsync(refreshToken, {
144
- secret: this.options.refreshSecret,
145
- algorithms: [JWT_ALGORITHM]
146
- });
453
+ try {
454
+ const payload = await this.jwtService.verifyAsync(
455
+ refreshToken,
456
+ {
457
+ secret: this.options.refreshSecret,
458
+ algorithms: [JWT_ALGORITHM],
459
+ ...this.options.jwtIssuer != null ? { issuer: this.options.jwtIssuer } : {},
460
+ ...this.options.jwtAudience != null ? { audience: this.options.jwtAudience } : {}
461
+ }
462
+ );
463
+ return assertRefreshTokenClaims(payload, this.options);
464
+ } catch (error) {
465
+ if (error instanceof UnauthorizedException) {
466
+ throw error;
467
+ }
468
+ throw new UnauthorizedException(AuthErrorCode.INVALID_REFRESH_TOKEN);
469
+ }
147
470
  }
148
471
  async hashRefreshToken(refreshToken) {
149
- return bcrypt2.hash(refreshToken, this.bcryptRounds);
472
+ return hashRefreshTokenValue(refreshToken, this.options.refreshSecret);
150
473
  }
151
- async compareRefreshToken(refreshToken, hash3) {
152
- return bcrypt2.compare(refreshToken, hash3);
474
+ async compareRefreshToken(refreshToken, hash2) {
475
+ return compareRefreshTokenValue(
476
+ refreshToken,
477
+ hash2,
478
+ this.options.refreshSecret
479
+ );
153
480
  }
154
481
  };
155
482
  TokenService = __decorateClass([
@@ -157,6 +484,32 @@ TokenService = __decorateClass([
157
484
  __decorateParam(0, Inject(JwtService)),
158
485
  __decorateParam(1, Inject(AUTH_MODULE_OPTIONS))
159
486
  ], TokenService);
487
+ function passwordChangedAtMs(account) {
488
+ return account.passwordChangedAt?.getTime();
489
+ }
490
+ function buildPwdAtClaim(account) {
491
+ return passwordChangedAtMs(account);
492
+ }
493
+ function isAccountLocked(account, lockoutEnabled) {
494
+ if (!lockoutEnabled) return false;
495
+ const lockedUntil = "lockedUntil" in account ? account.lockedUntil : void 0;
496
+ if (lockedUntil == null) return false;
497
+ return lockedUntil > /* @__PURE__ */ new Date();
498
+ }
499
+ function assertAccountNotLocked(account, options) {
500
+ if (!isAccountLocked(account, options.features?.accountLockout === true)) {
501
+ return;
502
+ }
503
+ throw new UnauthorizedException(AuthErrorCode.ACCOUNT_LOCKED);
504
+ }
505
+ function assertPasswordNotStale(payload, account) {
506
+ const changedAt = passwordChangedAtMs(account);
507
+ if (changedAt == null) return;
508
+ const tokenPwdAt = typeof payload.pwdAt === "number" ? payload.pwdAt : void 0;
509
+ if (tokenPwdAt == null || tokenPwdAt < changedAt) {
510
+ throw new UnauthorizedException(AuthErrorCode.PASSWORD_CHANGED);
511
+ }
512
+ }
160
513
 
161
514
  // src/services/auth.service.ts
162
515
  var AuthService = class {
@@ -205,7 +558,7 @@ var AuthService = class {
205
558
  resolveRegisterIdentifier(input, this.identifierField);
206
559
  this.assertRegisterEmailWhenVerificationEnabled(input);
207
560
  this.assertPasswordPolicy(dto.password);
208
- const passwordHash = await bcrypt2.hash(dto.password, this.bcryptRounds);
561
+ const passwordHash = await bcrypt.hash(dto.password, this.bcryptRounds);
209
562
  try {
210
563
  const account = await this.authRepository.create({
211
564
  ...input,
@@ -229,11 +582,8 @@ var AuthService = class {
229
582
  const account = await this.authRepository.findByIdentifierWithSecrets(
230
583
  identifier
231
584
  );
232
- if (account != null) {
233
- this.assertAccountNotLocked(account);
234
- }
235
585
  const passwordHash = account?.passwordHash ?? DUMMY_PASSWORD_HASH;
236
- const passwordMatches = await bcrypt2.compare(dto.password, passwordHash);
586
+ const passwordMatches = await bcrypt.compare(dto.password, passwordHash);
237
587
  if (account?.passwordHash == null || !passwordMatches) {
238
588
  if (account != null && this.accountLockoutEnabled) {
239
589
  await this.authRepository.recordLoginFailure(
@@ -243,6 +593,7 @@ var AuthService = class {
243
593
  }
244
594
  throw new UnauthorizedException(AuthErrorCode.INVALID_CREDENTIALS);
245
595
  }
596
+ assertAccountNotLocked(account, this.options);
246
597
  this.assertAccountActive(account);
247
598
  await this.authRepository.recordLoginSuccess(account.id);
248
599
  return this.issueTokens(account);
@@ -251,14 +602,25 @@ var AuthService = class {
251
602
  let payload;
252
603
  try {
253
604
  payload = await this.tokenService.verifyRefreshToken(refreshToken);
254
- } catch {
605
+ } catch (error) {
606
+ if (error instanceof UnauthorizedException) {
607
+ throw error;
608
+ }
255
609
  throw new UnauthorizedException(AuthErrorCode.INVALID_REFRESH_TOKEN);
256
610
  }
257
611
  const account = await this.authRepository.findByIdWithSecrets(payload.sub);
258
612
  if (account == null) {
259
613
  throw new UnauthorizedException(AuthErrorCode.INVALID_REFRESH_TOKEN);
260
614
  }
615
+ assertPasswordNotStale(
616
+ {
617
+ sub: payload.sub,
618
+ ...payload.pwdAt != null ? { pwdAt: payload.pwdAt } : {}
619
+ },
620
+ account
621
+ );
261
622
  this.assertAccountActive(account);
623
+ assertAccountNotLocked(account, this.options);
262
624
  if (this.rotateRefreshToken) {
263
625
  if (account.refreshTokenHash == null) {
264
626
  throw new UnauthorizedException(AuthErrorCode.INVALID_REFRESH_TOKEN);
@@ -271,6 +633,9 @@ var AuthService = class {
271
633
  await this.authRepository.updateRefreshTokenHash(account.id, null);
272
634
  throw new UnauthorizedException(AuthErrorCode.REFRESH_TOKEN_REUSE);
273
635
  }
636
+ return this.issueTokens(account, {
637
+ expectedRefreshHash: account.refreshTokenHash
638
+ });
274
639
  }
275
640
  return this.issueTokens(account);
276
641
  }
@@ -281,7 +646,7 @@ var AuthService = class {
281
646
  async me(authId) {
282
647
  const account = await this.authRepository.findById(authId);
283
648
  if (account == null) {
284
- throw new UnauthorizedException("Account not found");
649
+ throw new UnauthorizedException(AuthErrorCode.ACCOUNT_NOT_FOUND);
285
650
  }
286
651
  if (this.hooks.enrichMe != null) {
287
652
  return this.hooks.enrichMe(account);
@@ -322,18 +687,37 @@ var AuthService = class {
322
687
  throw new BadRequestException(AuthErrorCode.TOKEN_INVALID_OR_EXPIRED);
323
688
  }
324
689
  this.assertPasswordPolicy(newPassword);
325
- const passwordHash = await bcrypt2.hash(newPassword, this.bcryptRounds);
690
+ const samePassword = await bcrypt.compare(
691
+ newPassword,
692
+ account.passwordHash
693
+ );
694
+ if (samePassword) {
695
+ throw new BadRequestException(AuthErrorCode.PASSWORD_UNCHANGED);
696
+ }
697
+ const passwordHash = await bcrypt.hash(newPassword, this.bcryptRounds);
326
698
  await this.authRepository.updatePasswordHash(account.id, passwordHash);
327
699
  await this.authRepository.clearResetToken(account.id);
328
700
  await this.authRepository.updateRefreshTokenHash(account.id, null);
329
701
  return { reset: true };
330
702
  }
703
+ async resendVerification(email) {
704
+ this.assertEmailVerificationEnabled();
705
+ this.assertEmailHookWhenVerificationEnabled();
706
+ const normalizedEmail = normalizeIdentifier(email);
707
+ const account = await this.authRepository.findUnverifiedByEmail(
708
+ normalizedEmail
709
+ );
710
+ if (account != null && !account.disabled) {
711
+ await this.sendVerificationEmail(account);
712
+ }
713
+ return { sent: true };
714
+ }
331
715
  async changePassword(authId, currentPassword, newPassword) {
332
716
  const account = await this.authRepository.findByIdWithSecrets(authId);
333
717
  if (account?.passwordHash == null) {
334
718
  throw new UnauthorizedException(AuthErrorCode.INVALID_CURRENT_PASSWORD);
335
719
  }
336
- const currentMatches = await bcrypt2.compare(
720
+ const currentMatches = await bcrypt.compare(
337
721
  currentPassword,
338
722
  account.passwordHash
339
723
  );
@@ -344,19 +728,11 @@ var AuthService = class {
344
728
  throw new BadRequestException(AuthErrorCode.PASSWORD_UNCHANGED);
345
729
  }
346
730
  this.assertPasswordPolicy(newPassword);
347
- const passwordHash = await bcrypt2.hash(newPassword, this.bcryptRounds);
731
+ const passwordHash = await bcrypt.hash(newPassword, this.bcryptRounds);
348
732
  await this.authRepository.updatePasswordHash(account.id, passwordHash);
349
733
  await this.authRepository.updateRefreshTokenHash(account.id, null);
350
734
  return { changed: true };
351
735
  }
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
- }
360
736
  assertAccountActive(account) {
361
737
  if (account.disabled) {
362
738
  throw new UnauthorizedException(AuthErrorCode.ACCOUNT_DISABLED);
@@ -370,20 +746,33 @@ var AuthService = class {
370
746
  assertPasswordComplexity(password);
371
747
  }
372
748
  }
373
- async issueTokens(account) {
374
- const payload = await this.hooks.buildJwtPayload(account);
375
- const tokens = await this.tokenService.signTokens({
376
- ...payload,
377
- sub: account.id
378
- });
749
+ async issueTokens(account, rotation) {
750
+ const hookPayload = await this.hooks.buildJwtPayload(account);
751
+ const pwdAt = buildPwdAtClaim(account);
752
+ const tokens = await this.tokenService.signTokens(
753
+ buildAccessClaims(hookPayload, account.id, pwdAt, this.options),
754
+ buildRefreshClaims(account.id, pwdAt, this.options)
755
+ );
379
756
  if (this.rotateRefreshToken) {
380
757
  const refreshTokenHash = await this.tokenService.hashRefreshToken(
381
758
  tokens.refreshToken
382
759
  );
383
- await this.authRepository.updateRefreshTokenHash(
384
- account.id,
385
- refreshTokenHash
386
- );
760
+ if (rotation?.expectedRefreshHash != null) {
761
+ const swapped = await this.authRepository.rotateRefreshTokenHashIfMatch(
762
+ account.id,
763
+ rotation.expectedRefreshHash,
764
+ refreshTokenHash
765
+ );
766
+ if (!swapped) {
767
+ await this.authRepository.updateRefreshTokenHash(account.id, null);
768
+ throw new UnauthorizedException(AuthErrorCode.REFRESH_TOKEN_REUSE);
769
+ }
770
+ } else {
771
+ await this.authRepository.updateRefreshTokenHash(
772
+ account.id,
773
+ refreshTokenHash
774
+ );
775
+ }
387
776
  }
388
777
  await this.hooks.onAfterLogin?.(account);
389
778
  return tokens;
@@ -453,6 +842,21 @@ AuthService = __decorateClass([
453
842
  __decorateParam(2, Inject(AUTH_HOOKS)),
454
843
  __decorateParam(3, Inject(TokenService))
455
844
  ], AuthService);
845
+ function ApiAuthUnauthorizedResponse(...codes) {
846
+ const messageEnum = codes.length > 0 ? codes : Object.values(AuthErrorCode);
847
+ return applyDecorators(
848
+ ApiUnauthorizedResponse({
849
+ description: "Unauthorized \u2014 `message` is an `AuthErrorCode` value",
850
+ schema: {
851
+ type: "object",
852
+ properties: {
853
+ statusCode: { type: "number", example: 401 },
854
+ message: { type: "string", enum: messageEnum }
855
+ }
856
+ }
857
+ })
858
+ );
859
+ }
456
860
 
457
861
  // src/controllers/auth.controller.ts
458
862
  var AuthController = class {
@@ -477,6 +881,9 @@ var AuthController = class {
477
881
  verifyEmail(dto) {
478
882
  return this.authService.verifyEmail(dto.token);
479
883
  }
884
+ resendVerification(dto) {
885
+ return this.authService.resendVerification(dto.email);
886
+ }
480
887
  forgotPassword(dto) {
481
888
  return this.authService.forgotPassword(dto.email);
482
889
  }
@@ -493,52 +900,118 @@ var AuthController = class {
493
900
  };
494
901
  __decorateClass([
495
902
  Post("register"),
903
+ ApiOperation({ summary: "Register a new account" }),
904
+ ApiResponse({ status: 201, type: RegisterAckDto }),
496
905
  __decorateParam(0, Body())
497
906
  ], AuthController.prototype, "register", 1);
498
907
  __decorateClass([
499
908
  Post("login"),
909
+ HttpCode(HttpStatus.OK),
910
+ ApiOperation({ summary: "Login and receive JWT tokens" }),
911
+ ApiResponse({ status: 200, type: AuthTokensDto }),
912
+ ApiAuthUnauthorizedResponse(
913
+ AuthErrorCode.INVALID_CREDENTIALS,
914
+ AuthErrorCode.ACCOUNT_LOCKED,
915
+ AuthErrorCode.EMAIL_NOT_VERIFIED,
916
+ AuthErrorCode.ACCOUNT_DISABLED
917
+ ),
500
918
  __decorateParam(0, Body())
501
919
  ], AuthController.prototype, "login", 1);
502
920
  __decorateClass([
503
921
  Post("refresh"),
504
922
  HttpCode(HttpStatus.OK),
923
+ ApiOperation({ summary: "Refresh access token using refresh token" }),
924
+ ApiResponse({ status: 200, type: AuthTokensDto }),
925
+ ApiAuthUnauthorizedResponse(
926
+ AuthErrorCode.INVALID_REFRESH_TOKEN,
927
+ AuthErrorCode.REFRESH_TOKEN_REUSE,
928
+ AuthErrorCode.PASSWORD_CHANGED,
929
+ AuthErrorCode.EMAIL_NOT_VERIFIED,
930
+ AuthErrorCode.ACCOUNT_LOCKED
931
+ ),
505
932
  __decorateParam(0, Body())
506
933
  ], AuthController.prototype, "refresh", 1);
507
934
  __decorateClass([
508
935
  Post("logout"),
509
936
  UseGuards(JwtAuthGuard),
937
+ ApiBearerAuth("access-token"),
510
938
  HttpCode(HttpStatus.OK),
939
+ ApiOperation({ summary: "Logout and revoke refresh token" }),
940
+ ApiResponse({ status: 200, schema: { example: { loggedOut: true } } }),
941
+ ApiAuthUnauthorizedResponse(AuthErrorCode.UNAUTHORIZED),
511
942
  __decorateParam(0, CurrentUser())
512
943
  ], AuthController.prototype, "logout", 1);
513
944
  __decorateClass([
514
945
  Get("me"),
515
946
  UseGuards(JwtAuthGuard),
947
+ ApiBearerAuth("access-token"),
948
+ ApiOperation({ summary: "Get current authenticated user profile" }),
949
+ ApiResponse({ status: 200, type: MeResponseDto }),
950
+ ApiAuthUnauthorizedResponse(
951
+ AuthErrorCode.UNAUTHORIZED,
952
+ AuthErrorCode.ACCOUNT_NOT_FOUND,
953
+ AuthErrorCode.PASSWORD_CHANGED,
954
+ AuthErrorCode.EMAIL_NOT_VERIFIED,
955
+ AuthErrorCode.ACCOUNT_LOCKED
956
+ ),
516
957
  __decorateParam(0, CurrentUser())
517
958
  ], AuthController.prototype, "me", 1);
518
959
  __decorateClass([
519
960
  Post("verify-email"),
520
961
  HttpCode(HttpStatus.OK),
962
+ ApiOperation({
963
+ summary: "Verify email with token (requires emailVerification feature)"
964
+ }),
965
+ ApiResponse({ status: 200, schema: { example: { verified: true } } }),
966
+ ApiResponse({ status: 404, description: "Feature disabled" }),
521
967
  __decorateParam(0, Body())
522
968
  ], AuthController.prototype, "verifyEmail", 1);
969
+ __decorateClass([
970
+ Post("resend-verification"),
971
+ HttpCode(HttpStatus.OK),
972
+ ApiOperation({
973
+ summary: "Resend verification email (requires emailVerification feature)"
974
+ }),
975
+ ApiResponse({ status: 200, schema: { example: { sent: true } } }),
976
+ ApiResponse({ status: 404, description: "Feature disabled" }),
977
+ __decorateParam(0, Body())
978
+ ], AuthController.prototype, "resendVerification", 1);
523
979
  __decorateClass([
524
980
  Post("forgot-password"),
525
981
  HttpCode(HttpStatus.OK),
982
+ ApiOperation({
983
+ summary: "Request password reset email (requires passwordReset feature)"
984
+ }),
985
+ ApiResponse({ status: 200, schema: { example: { sent: true } } }),
986
+ ApiResponse({ status: 404, description: "Feature disabled" }),
526
987
  __decorateParam(0, Body())
527
988
  ], AuthController.prototype, "forgotPassword", 1);
528
989
  __decorateClass([
529
990
  Post("reset-password"),
530
991
  HttpCode(HttpStatus.OK),
992
+ ApiOperation({
993
+ summary: "Reset password with token (requires passwordReset feature)"
994
+ }),
995
+ ApiResponse({ status: 200, schema: { example: { reset: true } } }),
996
+ ApiResponse({ status: 404, description: "Feature disabled" }),
531
997
  __decorateParam(0, Body())
532
998
  ], AuthController.prototype, "resetPassword", 1);
533
999
  __decorateClass([
534
1000
  Post("change-password"),
535
1001
  UseGuards(JwtAuthGuard),
1002
+ ApiBearerAuth("access-token"),
536
1003
  HttpCode(HttpStatus.OK),
1004
+ ApiOperation({ summary: "Change password for authenticated user" }),
1005
+ ApiResponse({ status: 200, schema: { example: { changed: true } } }),
1006
+ ApiAuthUnauthorizedResponse(
1007
+ AuthErrorCode.UNAUTHORIZED,
1008
+ AuthErrorCode.INVALID_CURRENT_PASSWORD
1009
+ ),
537
1010
  __decorateParam(0, CurrentUser()),
538
1011
  __decorateParam(1, Body())
539
1012
  ], AuthController.prototype, "changePassword", 1);
540
1013
  AuthController = __decorateClass([
541
- Controller("auth"),
1014
+ ApiTags("auth"),
542
1015
  __decorateParam(0, Inject(AuthService))
543
1016
  ], AuthController);
544
1017
 
@@ -560,19 +1033,55 @@ var JwtStrategy = class extends PassportStrategy(Strategy) {
560
1033
  jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
561
1034
  ignoreExpiration: false,
562
1035
  secretOrKey: options.secret,
563
- algorithms: ["HS256"]
1036
+ algorithms: ["HS256"],
1037
+ ...options.jwtIssuer != null ? { issuer: options.jwtIssuer } : {},
1038
+ ...options.jwtAudience != null ? { audience: options.jwtAudience } : {}
564
1039
  });
565
1040
  this.options = options;
566
1041
  this.authRepository = authRepository;
1042
+ this.validationCache = /* @__PURE__ */ new Map();
567
1043
  }
568
- async validate(payload) {
569
- const account = await this.authRepository.findById(payload.sub);
570
- if (account == null || account.disabled) {
571
- throw new UnauthorizedException("Account not found or inactive");
1044
+ get cacheTtlMs() {
1045
+ return this.options.jwtValidationCacheTtlMs ?? 0;
1046
+ }
1047
+ getCachedAccount(sub) {
1048
+ const cached = this.validationCache.get(sub);
1049
+ if (cached == null || cached.expiresAt <= Date.now()) {
1050
+ if (cached != null) this.validationCache.delete(sub);
1051
+ return null;
1052
+ }
1053
+ return cached.account;
1054
+ }
1055
+ cacheAccount(sub, account) {
1056
+ if (this.cacheTtlMs <= 0) return;
1057
+ this.validationCache.set(sub, {
1058
+ account,
1059
+ expiresAt: Date.now() + this.cacheTtlMs
1060
+ });
1061
+ }
1062
+ assertAccountActive(account, payload) {
1063
+ if (account.disabled) {
1064
+ throw new UnauthorizedException(AuthErrorCode.ACCOUNT_DISABLED);
572
1065
  }
1066
+ assertAccountNotLocked(account, this.options);
573
1067
  if (this.options.features?.emailVerification === true && !account.emailVerified) {
574
1068
  throw new UnauthorizedException(AuthErrorCode.EMAIL_NOT_VERIFIED);
575
1069
  }
1070
+ assertPasswordNotStale(payload, account);
1071
+ }
1072
+ async validate(payload) {
1073
+ assertAccessTokenClaims(payload, this.options);
1074
+ const cached = this.getCachedAccount(payload.sub);
1075
+ if (cached != null) {
1076
+ this.assertAccountActive(cached, payload);
1077
+ return payload;
1078
+ }
1079
+ const account = await this.authRepository.findById(payload.sub);
1080
+ if (account == null) {
1081
+ throw new UnauthorizedException(AuthErrorCode.ACCOUNT_NOT_FOUND);
1082
+ }
1083
+ this.assertAccountActive(account, payload);
1084
+ this.cacheAccount(payload.sub, account);
576
1085
  return payload;
577
1086
  }
578
1087
  };
@@ -581,8 +1090,6 @@ JwtStrategy = __decorateClass([
581
1090
  __decorateParam(0, Inject(AUTH_MODULE_OPTIONS)),
582
1091
  __decorateParam(1, Inject(AUTH_REPOSITORY))
583
1092
  ], JwtStrategy);
584
-
585
- // src/utils/hooks-provider.util.ts
586
1093
  function createHooksProvider(options) {
587
1094
  if (options.hooksProvider != null) {
588
1095
  return options.hooksProvider;
@@ -590,7 +1097,8 @@ function createHooksProvider(options) {
590
1097
  const HooksClass = options.hooks ?? DefaultAuthHooks;
591
1098
  return {
592
1099
  provide: AUTH_HOOKS,
593
- useClass: HooksClass
1100
+ inject: [ModuleRef],
1101
+ useFactory: (moduleRef) => moduleRef.create(HooksClass)
594
1102
  };
595
1103
  }
596
1104
 
@@ -630,6 +1138,17 @@ function validateAuthModuleOptions(options) {
630
1138
  "AuthModule: passwordResetTokenTtlMs must be at least 60000 (1 minute)"
631
1139
  );
632
1140
  }
1141
+ if (options.features?.refreshTokenRotation === false) {
1142
+ console.warn(
1143
+ "[aranza-auth] features.refreshTokenRotation is false \u2014 refresh tokens are stateless until JWT expiry; stolen tokens cannot be revoked server-side."
1144
+ );
1145
+ }
1146
+ const cacheTtl = options.jwtValidationCacheTtlMs ?? 0;
1147
+ if (cacheTtl < 0 || cacheTtl > 3e5) {
1148
+ throw new Error(
1149
+ "AuthModule: jwtValidationCacheTtlMs must be between 0 and 300000 (5 minutes)"
1150
+ );
1151
+ }
633
1152
  }
634
1153
 
635
1154
  // src/auth.module.ts
@@ -647,15 +1166,33 @@ function createCoreProviders(options) {
647
1166
  JwtAuthGuard
648
1167
  ];
649
1168
  }
650
- function createAsyncHooksProvider(options) {
651
- if (options.hooksProvider != null) {
652
- return options.hooksProvider;
653
- }
654
- const HooksClass = options.hooks ?? DefaultAuthHooks;
1169
+ function createAsyncProviders(options) {
1170
+ return [
1171
+ {
1172
+ provide: AUTH_MODULE_OPTIONS,
1173
+ inject: options.inject ?? [],
1174
+ useFactory: async (...args) => {
1175
+ const config = await options.useFactory(...args);
1176
+ validateAuthModuleOptions(config);
1177
+ return config;
1178
+ }
1179
+ },
1180
+ createHooksProvider(options),
1181
+ AuthService,
1182
+ TokenService,
1183
+ JwtStrategy,
1184
+ JwtAuthGuard
1185
+ ];
1186
+ }
1187
+ function jwtModuleOptions(opts) {
655
1188
  return {
656
- provide: AUTH_HOOKS,
657
- inject: [ModuleRef],
658
- useFactory: (moduleRef) => moduleRef.create(HooksClass)
1189
+ secret: opts.secret,
1190
+ signOptions: {
1191
+ expiresIn: opts.expiresIn ?? "1h",
1192
+ algorithm: "HS256",
1193
+ ...opts.jwtIssuer != null ? { issuer: opts.jwtIssuer } : {},
1194
+ ...opts.jwtAudience != null ? { audience: opts.jwtAudience } : {}
1195
+ }
659
1196
  };
660
1197
  }
661
1198
  function createAuthImports() {
@@ -663,13 +1200,7 @@ function createAuthImports() {
663
1200
  PassportModule.register({ defaultStrategy: "jwt" }),
664
1201
  JwtModule.registerAsync({
665
1202
  inject: [AUTH_MODULE_OPTIONS],
666
- useFactory: (opts) => ({
667
- secret: opts.secret,
668
- signOptions: {
669
- expiresIn: opts.expiresIn ?? "1h",
670
- algorithm: "HS256"
671
- }
672
- })
1203
+ useFactory: (opts) => jwtModuleOptions(opts)
673
1204
  })
674
1205
  ];
675
1206
  }
@@ -680,24 +1211,6 @@ function mergeImports(userImports) {
680
1211
  }
681
1212
  return merged;
682
1213
  }
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
- }
701
1214
  var AuthModule = class {
702
1215
  static forRoot(options) {
703
1216
  const routePrefix = options.routePrefix ?? "auth";
@@ -742,90 +1255,6 @@ AuthModule = __decorateClass([
742
1255
  Module({})
743
1256
  ], AuthModule);
744
1257
 
745
- // src/dto/auth-tokens.dto.ts
746
- var AuthTokensDto = class {
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);
759
- var ForgotPasswordDto = class {
760
- };
761
- __decorateClass([
762
- IsEmail()
763
- ], ForgotPasswordDto.prototype, "email", 2);
764
- var LoginDto = class {
765
- };
766
- __decorateClass([
767
- IsOptional(),
768
- ValidateIf((dto) => dto.email != null && dto.email.trim() !== ""),
769
- IsEmail(),
770
- Length(3, 255)
771
- ], LoginDto.prototype, "email", 2);
772
- __decorateClass([
773
- IsOptional(),
774
- IsString(),
775
- Length(3, 50)
776
- ], LoginDto.prototype, "username", 2);
777
- __decorateClass([
778
- IsString(),
779
- IsNotEmpty(),
780
- Length(8, 128)
781
- ], LoginDto.prototype, "password", 2);
782
- var RefreshTokenDto = class {
783
- };
784
- __decorateClass([
785
- IsString(),
786
- IsNotEmpty()
787
- ], RefreshTokenDto.prototype, "refreshToken", 2);
788
-
789
- // src/dto/register-ack.dto.ts
790
- var RegisterAckDto = class {
791
- };
792
- var RegisterDto = class {
793
- };
794
- __decorateClass([
795
- IsOptional(),
796
- ValidateIf((dto) => dto.email != null && dto.email.trim() !== ""),
797
- IsEmail(),
798
- Length(3, 255)
799
- ], RegisterDto.prototype, "email", 2);
800
- __decorateClass([
801
- IsOptional(),
802
- IsString(),
803
- Length(3, 50),
804
- Matches(/^[a-zA-Z0-9._-]+$/)
805
- ], RegisterDto.prototype, "username", 2);
806
- __decorateClass([
807
- IsString(),
808
- IsNotEmpty(),
809
- Length(8, 128)
810
- ], RegisterDto.prototype, "password", 2);
811
- var ResetPasswordDto = class {
812
- };
813
- __decorateClass([
814
- IsString(),
815
- IsNotEmpty()
816
- ], ResetPasswordDto.prototype, "token", 2);
817
- __decorateClass([
818
- IsString(),
819
- IsNotEmpty(),
820
- Length(8, 128)
821
- ], ResetPasswordDto.prototype, "newPassword", 2);
822
- var VerifyEmailDto = class {
823
- };
824
- __decorateClass([
825
- IsString(),
826
- IsNotEmpty()
827
- ], VerifyEmailDto.prototype, "token", 2);
828
-
829
- export { AUTH_RATE_LIMIT_PRESETS, AuthErrorCode, AuthModule, AuthService, AuthTokensDto, ChangePasswordDto, CurrentUser, DefaultAuthHooks, ForgotPasswordDto, JwtAuthGuard, LoginDto, RefreshTokenDto, RegisterAckDto, RegisterDto, ResetPasswordDto, TokenService, VerifyEmailDto };
1258
+ export { AUTH_RATE_LIMIT_PRESETS, AUTH_RATE_LIMIT_ROUTES, AuthErrorCode, AuthModule, AuthService, AuthTokensDto, ChangePasswordDto, CurrentUser, DefaultAuthHooks, ForgotPasswordDto, JwtAuthGuard, LoginDto, MeResponseDto, RefreshTokenDto, RegisterAckDto, RegisterDto, ResendVerificationDto, ResetPasswordDto, TokenService, VerifyEmailDto, buildClearRefreshTokenCookie, buildRefreshTokenCookie, setupAuthSwagger };
830
1259
  //# sourceMappingURL=index.js.map
831
1260
  //# sourceMappingURL=index.js.map