@ackplus/nest-auth 1.1.18 → 1.1.20

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 (51) hide show
  1. package/package.json +1 -1
  2. package/src/lib/admin-console/static/index.html +697 -177
  3. package/src/lib/audit/services/audit.service.d.ts +15 -0
  4. package/src/lib/audit/services/audit.service.d.ts.map +1 -0
  5. package/src/lib/audit/services/audit.service.js +143 -0
  6. package/src/lib/auth/controllers/auth.controller.d.ts +1 -1
  7. package/src/lib/auth/controllers/mfa.controller.js +5 -5
  8. package/src/lib/auth/dto/responses/mfa-status.response.dto.d.ts +2 -2
  9. package/src/lib/auth/dto/responses/mfa-status.response.dto.d.ts.map +1 -1
  10. package/src/lib/auth/dto/responses/mfa-status.response.dto.js +5 -5
  11. package/src/lib/auth/events/index.d.ts +13 -0
  12. package/src/lib/auth/events/index.d.ts.map +1 -0
  13. package/src/lib/auth/events/index.js +15 -0
  14. package/src/lib/auth/events/user-2fa-disabled.event.d.ts +10 -0
  15. package/src/lib/auth/events/user-2fa-disabled.event.d.ts.map +1 -0
  16. package/src/lib/auth/events/user-2fa-disabled.event.js +12 -0
  17. package/src/lib/auth/events/user-2fa-enabled.event.d.ts +13 -0
  18. package/src/lib/auth/events/user-2fa-enabled.event.d.ts.map +1 -0
  19. package/src/lib/auth/events/user-2fa-enabled.event.js +15 -0
  20. package/src/lib/auth/events/user-password-changed.event.d.ts +12 -0
  21. package/src/lib/auth/events/user-password-changed.event.d.ts.map +1 -0
  22. package/src/lib/auth/events/user-password-changed.event.js +15 -0
  23. package/src/lib/auth/guards/auth.guard.d.ts +19 -1
  24. package/src/lib/auth/guards/auth.guard.d.ts.map +1 -1
  25. package/src/lib/auth/guards/auth.guard.js +113 -25
  26. package/src/lib/auth/services/auth.service.d.ts +10 -6
  27. package/src/lib/auth/services/auth.service.d.ts.map +1 -1
  28. package/src/lib/auth/services/auth.service.js +313 -133
  29. package/src/lib/auth/services/mfa.service.d.ts +1 -1
  30. package/src/lib/auth/services/mfa.service.d.ts.map +1 -1
  31. package/src/lib/auth/services/mfa.service.js +46 -10
  32. package/src/lib/auth.constants.d.ts +181 -8
  33. package/src/lib/auth.constants.d.ts.map +1 -1
  34. package/src/lib/auth.constants.js +142 -10
  35. package/src/lib/core/interfaces/auth-module-options.interface.d.ts +170 -0
  36. package/src/lib/core/interfaces/auth-module-options.interface.d.ts.map +1 -1
  37. package/src/lib/core/interfaces/session-options.interface.d.ts +52 -0
  38. package/src/lib/core/interfaces/session-options.interface.d.ts.map +1 -1
  39. package/src/lib/core/interfaces/token-payload.interface.d.ts +14 -6
  40. package/src/lib/core/interfaces/token-payload.interface.d.ts.map +1 -1
  41. package/src/lib/core/services/auth-config.service.js +1 -1
  42. package/src/lib/nest-auth.module.d.ts.map +1 -1
  43. package/src/lib/nest-auth.module.js +5 -2
  44. package/src/lib/session/services/session-manager.service.d.ts +6 -6
  45. package/src/lib/session/services/session-manager.service.d.ts.map +1 -1
  46. package/src/lib/session/services/session-manager.service.js +54 -21
  47. package/src/lib/user/entities/user.entity.d.ts.map +1 -1
  48. package/src/lib/user/entities/user.entity.js +19 -0
  49. package/src/lib/user/services/user.service.d.ts +8 -6
  50. package/src/lib/user/services/user.service.d.ts.map +1 -1
  51. package/src/lib/user/services/user.service.js +51 -46
@@ -30,8 +30,10 @@ const debug_logger_service_1 = require("../../core/services/debug-logger.service
30
30
  const moment_1 = tslib_1.__importDefault(require("moment"));
31
31
  const auth_config_service_1 = require("../../core/services/auth-config.service");
32
32
  const cookie_helper_1 = require("../../utils/cookie.helper");
33
+ const user_password_changed_event_1 = require("../events/user-password-changed.event");
34
+ const user_service_1 = require("../../user/services/user.service");
33
35
  let AuthService = class AuthService {
34
- constructor(userRepository, otpRepository, authProviderRegistry, mfaService, sessionManager, jwtService, eventEmitter, tenantService, debugLogger, authConfigService) {
36
+ constructor(userRepository, otpRepository, authProviderRegistry, mfaService, sessionManager, jwtService, eventEmitter, tenantService, debugLogger, authConfigService, userService) {
35
37
  this.userRepository = userRepository;
36
38
  this.otpRepository = otpRepository;
37
39
  this.authProviderRegistry = authProviderRegistry;
@@ -42,6 +44,7 @@ let AuthService = class AuthService {
42
44
  this.tenantService = tenantService;
43
45
  this.debugLogger = debugLogger;
44
46
  this.authConfigService = authConfigService;
47
+ this.userService = userService;
45
48
  }
46
49
  getUserWithRolesAndPermissions(userId, relations = []) {
47
50
  return this.userRepository.findOne({
@@ -57,14 +60,23 @@ let AuthService = class AuthService {
57
60
  if (!user) {
58
61
  return null;
59
62
  }
60
- return this.getUserWithRolesAndPermissions(user.id);
63
+ const fullUser = await this.getUserWithRolesAndPermissions(user.id);
64
+ // Apply user.serialize hook if configured
65
+ const config = this.authConfigService.getConfig();
66
+ if (config.user?.serialize) {
67
+ return await config.user.serialize(fullUser);
68
+ }
69
+ return fullUser;
61
70
  }
62
71
  async signup(input) {
63
72
  this.debugLogger.logFunctionEntry('signup', 'AuthService', { email: input.email, phone: input.phone, hasPassword: !!input.password });
64
73
  try {
65
74
  const config = this.authConfigService.getConfig();
66
75
  if (config.registration?.enabled === false) {
67
- throw new common_1.ForbiddenException('Registration is disabled');
76
+ throw new common_1.ForbiddenException({
77
+ message: 'Registration is disabled',
78
+ code: auth_constants_1.ERROR_CODES.REGISTRATION_DISABLED,
79
+ });
68
80
  }
69
81
  const { email, phone, password } = input;
70
82
  let { tenantId = null } = input;
@@ -73,7 +85,10 @@ let AuthService = class AuthService {
73
85
  this.debugLogger.logAuthOperation('signup', 'email|phone', undefined, { email, phone, resolvedTenantId: tenantId });
74
86
  if (!email && !phone) {
75
87
  this.debugLogger.error('Signup failed: Neither email nor phone provided', 'AuthService');
76
- throw new common_1.BadRequestException('Either email or phone must be provided');
88
+ throw new common_1.BadRequestException({
89
+ message: 'Either email or phone must be provided',
90
+ code: auth_constants_1.ERROR_CODES.EMAIL_OR_PHONE_REQUIRED,
91
+ });
77
92
  }
78
93
  let provider = null;
79
94
  let providerUserId = null;
@@ -87,31 +102,42 @@ let AuthService = class AuthService {
87
102
  }
88
103
  if (!provider) {
89
104
  this.debugLogger.error('Provider not found for signup', 'AuthService', { email: !!email, phone: !!phone });
90
- throw new common_1.InternalServerErrorException('Phone or email authentication is not enabled');
105
+ throw new common_1.InternalServerErrorException({
106
+ message: 'Phone or email authentication is not enabled',
107
+ code: auth_constants_1.ERROR_CODES.PROVIDER_NOT_FOUND,
108
+ });
91
109
  }
92
110
  this.debugLogger.debug('Checking for existing identity', 'AuthService', { providerUserId });
93
111
  const identity = await provider.findIdentity(providerUserId);
94
112
  if (identity) {
95
113
  this.debugLogger.warn('Identity already exists', 'AuthService', { email: !!email, phone: !!phone, tenantId });
96
114
  if (email) {
97
- throw new common_1.BadRequestException('Email already exists in this tenant');
115
+ throw new common_1.BadRequestException({
116
+ message: 'Email already exists in this tenant',
117
+ code: auth_constants_1.ERROR_CODES.EMAIL_ALREADY_EXISTS,
118
+ });
98
119
  }
99
120
  if (phone) {
100
- throw new common_1.BadRequestException('Phone number already exists in this tenant');
121
+ throw new common_1.BadRequestException({
122
+ message: 'Phone number already exists in this tenant',
123
+ code: auth_constants_1.ERROR_CODES.PHONE_ALREADY_EXISTS,
124
+ });
101
125
  }
102
126
  }
103
- this.debugLogger.debug('Creating new user', 'AuthService', { email: !!email, phone: !!phone, tenantId });
104
- let user = this.userRepository.create({
127
+ this.debugLogger.debug('Creating new user via UserService', 'AuthService', { email: !!email, phone: !!phone, tenantId });
128
+ // Use UserService to create user, which handles hooks and password hashing
129
+ // We pass the plain password, UserService will hash it if provided
130
+ let user = await this.userService.createUser({
105
131
  email,
106
132
  phone,
107
133
  tenantId,
108
134
  isVerified: false,
109
- });
110
- await user.setPassword(password);
111
- await this.userRepository.save(user);
135
+ password
136
+ }, input);
112
137
  this.debugLogger.info('User created successfully', 'AuthService', { userId: user.id, tenantId });
113
138
  user = await this.getUserWithRolesAndPermissions(user.id);
114
139
  this.debugLogger.debug('Linking user to provider', 'AuthService', { userId: user.id, providerName: provider.providerName });
140
+ // Note: UserService might have already created the identity, but we ensure it's linked here
115
141
  await provider.linkToUser(user.id, providerUserId);
116
142
  this.debugLogger.debug('Creating session for new user', 'AuthService', { userId: user.id });
117
143
  const session = await this.sessionManager.createSessionFromUser(user);
@@ -130,14 +156,21 @@ let AuthService = class AuthService {
130
156
  isRequiresMfa
131
157
  }));
132
158
  this.debugLogger.logFunctionExit('signup', 'AuthService', { userId: user.id, isRequiresMfa });
133
- return {
159
+ // Build default response
160
+ let response = {
134
161
  accessToken: tokens.accessToken,
135
162
  refreshToken: tokens.refreshToken,
136
163
  isRequiresMfa: isRequiresMfa,
137
164
  };
165
+ // Apply auth.transformResponse hook if configured
166
+ if (config.auth?.transformResponse) {
167
+ response = await config.auth.transformResponse(response, user, session);
168
+ }
169
+ return response;
138
170
  }
139
171
  catch (error) {
140
172
  this.debugLogger.logError(error, 'signup', { email: input.email, phone: input.phone });
173
+ this.handleError(error, 'signup');
141
174
  throw error;
142
175
  }
143
176
  }
@@ -145,76 +178,100 @@ let AuthService = class AuthService {
145
178
  const { credentials, providerName, createUserIfNotExists = false } = input;
146
179
  this.debugLogger.logFunctionEntry('login', 'AuthService', { providerName, createUserIfNotExists });
147
180
  let { tenantId = null } = input;
148
- // Resolve tenant ID - use provided or default
149
- tenantId = await this.tenantService.resolveTenantId(tenantId);
150
- this.debugLogger.logAuthOperation('login', providerName, undefined, { resolvedTenantId: tenantId, createUserIfNotExists });
151
- const provider = this.authProviderRegistry.getProvider(providerName);
152
- if (!provider) {
153
- throw new common_1.UnauthorizedException('Invalid authentication providerName or provider is not enabled');
154
- }
155
- const requiredFields = provider.getRequiredFields();
156
- if (!requiredFields.every(field => credentials[field])) {
157
- throw new common_1.BadRequestException(`Missing ${requiredFields.join(', ')} required fields`);
158
- }
159
- const authProviderUser = await provider.validate(credentials);
160
- const identity = await provider.findIdentity(authProviderUser.userId);
161
- let user = identity?.user || null;
162
- if (!user) {
163
- if (!createUserIfNotExists) {
164
- throw new common_1.UnauthorizedException('Invalid credentials');
181
+ try {
182
+ // Resolve tenant ID - use provided or default
183
+ tenantId = await this.tenantService.resolveTenantId(tenantId);
184
+ this.debugLogger.logAuthOperation('login', providerName, undefined, { resolvedTenantId: tenantId, createUserIfNotExists });
185
+ const provider = this.authProviderRegistry.getProvider(providerName);
186
+ if (!provider) {
187
+ throw new common_1.UnauthorizedException({
188
+ message: 'Invalid authentication providerName or provider is not enabled',
189
+ code: auth_constants_1.ERROR_CODES.INVALID_PROVIDER,
190
+ });
165
191
  }
166
- // Create new user if not exists and link to provider
167
- user = await this.handleSocialLogin(provider, authProviderUser, tenantId);
168
- }
169
- if (user.isActive === false) {
170
- throw new common_1.UnauthorizedException({
171
- message: 'Your account is suspended, please contact support',
172
- code: auth_constants_1.USER_NOT_ACTIVE_ERROR,
173
- });
174
- }
175
- user = await this.getUserWithRolesAndPermissions(user.id);
176
- const session = await this.sessionManager.createSessionFromUser(user);
177
- const tokens = await this.generateTokensFromSession(session);
178
- let isRequiresMfa = await this.mfaService.isRequiresMfa(user.id);
179
- // Check for trusted device cookie or header if MFA is required
180
- if (isRequiresMfa) {
181
- // Set Mfa enbale if requred for all user, set in properly in session
182
- await this.sessionManager.updateSession(session.id, {
183
- data: { ...session.data, isMfaEnabled: true }
184
- });
185
- const trustCookieName = auth_config_service_1.AuthConfigService.getOptions().mfa?.trustDeviceStorageName || auth_constants_1.NEST_AUTH_TRUST_DEVICE_KEY;
186
- const req = request_context_1.RequestContext.currentRequest();
187
- let trustToken = cookie_helper_1.CookieHelper.get(req, trustCookieName);
188
- // If not in cookie, check header
189
- if (!trustToken) {
190
- trustToken = req.headers[trustCookieName];
191
- }
192
- if (trustToken) {
193
- const isTrusted = await this.mfaService.validateTrustedDevice(user.id, trustToken);
194
- if (isTrusted) {
195
- isRequiresMfa = false;
196
- // Update session to indicate MFA is verified by trust
197
- await this.sessionManager.updateSession(session.id, {
198
- data: { ...session.data, isMfaVerified: true }
192
+ const requiredFields = provider.getRequiredFields();
193
+ if (!requiredFields.every(field => credentials[field])) {
194
+ throw new common_1.BadRequestException({
195
+ message: `Missing ${requiredFields.join(', ')} required fields`,
196
+ code: auth_constants_1.ERROR_CODES.MISSING_REQUIRED_FIELDS,
197
+ });
198
+ }
199
+ const authProviderUser = await provider.validate(credentials);
200
+ const identity = await provider.findIdentity(authProviderUser.userId);
201
+ let user = identity?.user || null;
202
+ if (!user) {
203
+ if (!createUserIfNotExists) {
204
+ throw new common_1.UnauthorizedException({
205
+ message: 'Invalid credentials',
206
+ code: auth_constants_1.ERROR_CODES.INVALID_CREDENTIALS,
199
207
  });
200
208
  }
209
+ // Create new user if not exists and link to provider
210
+ user = await this.handleSocialLogin(provider, authProviderUser, tenantId);
211
+ }
212
+ if (user.isActive === false) {
213
+ throw new common_1.UnauthorizedException({
214
+ message: 'Your account is suspended, please contact support',
215
+ code: auth_constants_1.ERROR_CODES.ACCOUNT_INACTIVE,
216
+ });
201
217
  }
218
+ user = await this.getUserWithRolesAndPermissions(user.id);
219
+ let isRequiresMfa = await this.mfaService.isRequiresMfa(user.id);
220
+ user.isMfaEnabled = isRequiresMfa;
221
+ let session = await this.sessionManager.createSessionFromUser(user);
222
+ // Check for trusted device cookie or header if MFA is required
223
+ if (isRequiresMfa) {
224
+ // Set Mfa enbale if requred for all user, set in properly in session
225
+ await this.sessionManager.updateSession(session.id, {
226
+ data: { ...session.data, isMfaEnabled: true }
227
+ });
228
+ const trustCookieName = auth_config_service_1.AuthConfigService.getOptions().mfa?.trustDeviceStorageName || auth_constants_1.NEST_AUTH_TRUST_DEVICE_KEY;
229
+ const req = request_context_1.RequestContext.currentRequest();
230
+ let trustToken = cookie_helper_1.CookieHelper.get(req, trustCookieName);
231
+ // If not in cookie, check header
232
+ if (!trustToken) {
233
+ trustToken = req.headers[trustCookieName];
234
+ }
235
+ if (trustToken) {
236
+ const isTrusted = await this.mfaService.validateTrustedDevice(user.id, trustToken);
237
+ if (isTrusted) {
238
+ isRequiresMfa = false;
239
+ // Update session to indicate MFA is verified by trust
240
+ session = await this.sessionManager.updateSession(session.id, {
241
+ data: { ...session.data, isMfaVerified: true }
242
+ });
243
+ }
244
+ }
245
+ }
246
+ const tokens = await this.generateTokensFromSession(session);
247
+ // Emit login event
248
+ await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.LOGGED_IN, new user_logged_in_event_1.UserLoggedInEvent({
249
+ user,
250
+ tenantId: user.tenantId,
251
+ input,
252
+ provider,
253
+ session,
254
+ tokens,
255
+ isRequiresMfa
256
+ }));
257
+ // Build default response
258
+ let response = {
259
+ accessToken: tokens.accessToken,
260
+ refreshToken: tokens.refreshToken,
261
+ isRequiresMfa: isRequiresMfa,
262
+ };
263
+ // Apply auth.transformResponse hook if configured
264
+ const config = this.authConfigService.getConfig();
265
+ if (config.auth?.transformResponse) {
266
+ response = await config.auth.transformResponse(response, user, session);
267
+ }
268
+ return response;
269
+ }
270
+ catch (error) {
271
+ this.debugLogger.logError(error, 'login', { providerName, createUserIfNotExists });
272
+ this.handleError(error, 'login');
273
+ throw error;
202
274
  }
203
- // Emit login event
204
- await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.LOGGED_IN, new user_logged_in_event_1.UserLoggedInEvent({
205
- user,
206
- tenantId: user.tenantId,
207
- input,
208
- provider,
209
- session,
210
- tokens,
211
- isRequiresMfa
212
- }));
213
- return {
214
- accessToken: tokens.accessToken,
215
- refreshToken: tokens.refreshToken,
216
- isRequiresMfa: isRequiresMfa,
217
- };
218
275
  }
219
276
  async verify2fa(input) {
220
277
  this.debugLogger.logFunctionEntry('verify2fa', 'AuthService', { method: input.method });
@@ -224,7 +281,7 @@ let AuthService = class AuthService {
224
281
  this.debugLogger.error('Session not found for 2FA verification', 'AuthService');
225
282
  throw new common_1.UnauthorizedException({
226
283
  message: 'Session not found',
227
- code: auth_constants_1.SESSION_NOT_FOUND_ERROR,
284
+ code: auth_constants_1.ERROR_CODES.SESSION_NOT_FOUND,
228
285
  });
229
286
  }
230
287
  this.debugLogger.debug('Verifying MFA code', 'AuthService', { userId: session.userId, method: input.method });
@@ -233,7 +290,7 @@ let AuthService = class AuthService {
233
290
  this.debugLogger.warn('Invalid MFA code provided', 'AuthService', { userId: session.userId, method: input.method });
234
291
  throw new common_1.UnauthorizedException({
235
292
  message: 'Invalid MFA code',
236
- code: auth_constants_1.INVALID_MFA_EXCEPTION_CODE,
293
+ code: auth_constants_1.ERROR_CODES.MFA_CODE_INVALID,
237
294
  });
238
295
  }
239
296
  this.debugLogger.debug('Updating session with MFA verification', 'AuthService', { sessionId: session.id });
@@ -255,7 +312,7 @@ let AuthService = class AuthService {
255
312
  // Emit 2FA verified event
256
313
  this.debugLogger.debug('Emitting 2FA verified event', 'AuthService', { userId: user.id });
257
314
  await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.TWO_FACTOR_VERIFIED, new user_2fa_verified_event_1.User2faVerifiedEvent({
258
- user,
315
+ user: user,
259
316
  tenantId: user.tenantId,
260
317
  input,
261
318
  session,
@@ -270,13 +327,17 @@ let AuthService = class AuthService {
270
327
  }
271
328
  catch (error) {
272
329
  this.debugLogger.logError(error, 'verify2fa', { method: input.method });
330
+ this.handleError(error, 'mfa');
273
331
  throw error;
274
332
  }
275
333
  }
276
334
  async send2faCode(userId, method) {
277
335
  const user = await this.userRepository.findOne({ where: { id: userId } });
278
336
  if (!user) {
279
- throw new common_1.UnauthorizedException('User not found');
337
+ throw new common_1.UnauthorizedException({
338
+ message: 'User not found',
339
+ code: auth_constants_1.ERROR_CODES.USER_NOT_FOUND,
340
+ });
280
341
  }
281
342
  await this.mfaService.sendMfaCode(user.id, method);
282
343
  return true;
@@ -309,7 +370,7 @@ let AuthService = class AuthService {
309
370
  this.debugLogger.error('No refresh token provided', 'AuthService');
310
371
  throw new common_1.UnauthorizedException({
311
372
  message: 'No refresh token provided',
312
- code: auth_constants_1.REFRESH_TOKEN_INVALID,
373
+ code: auth_constants_1.ERROR_CODES.REFRESH_TOKEN_INVALID,
313
374
  });
314
375
  }
315
376
  this.debugLogger.debug('Verifying refresh token', 'AuthService');
@@ -321,20 +382,18 @@ let AuthService = class AuthService {
321
382
  this.debugLogger.warn('Invalid or expired refresh token', 'AuthService');
322
383
  throw new common_1.UnauthorizedException({
323
384
  message: 'Invalid or expired refresh token',
324
- code: auth_constants_1.REFRESH_TOKEN_EXPIRED,
385
+ code: auth_constants_1.ERROR_CODES.REFRESH_TOKEN_EXPIRED,
325
386
  });
326
387
  }
327
388
  const session = await this.sessionManager.getSession(payload.sessionId);
328
389
  if (!session) {
329
390
  throw new common_1.UnauthorizedException({
330
391
  message: 'Invalid refresh token',
331
- code: auth_constants_1.REFRESH_TOKEN_INVALID,
392
+ code: auth_constants_1.ERROR_CODES.REFRESH_TOKEN_INVALID,
332
393
  });
333
394
  }
334
- // Generate new session
335
- const newSession = await this.sessionManager.createSessionFromSession(session);
336
- // Revoke old session
337
- await this.sessionManager.revokeSession(session.id);
395
+ // Refresh existing session
396
+ const newSession = await this.sessionManager.refreshSession(session);
338
397
  // Generate new tokens
339
398
  this.debugLogger.debug('Generating new tokens from refreshed session', 'AuthService', { sessionId: newSession.id });
340
399
  const tokens = await this.generateTokensFromSession(newSession);
@@ -350,6 +409,7 @@ let AuthService = class AuthService {
350
409
  }
351
410
  catch (error) {
352
411
  this.debugLogger.logError(error, 'refreshToken', { hasRefreshToken: !!refreshToken });
412
+ this.handleError(error, 'refresh');
353
413
  throw error;
354
414
  }
355
415
  }
@@ -358,20 +418,32 @@ let AuthService = class AuthService {
358
418
  try {
359
419
  const currentUser = request_context_1.RequestContext.currentUser();
360
420
  if (!currentUser?.id) {
361
- throw new common_1.UnauthorizedException('User not found');
421
+ throw new common_1.UnauthorizedException({
422
+ message: 'User not found',
423
+ code: auth_constants_1.ERROR_CODES.USER_NOT_FOUND,
424
+ });
362
425
  }
363
426
  const user = await this.userRepository.findOne({
364
427
  where: { id: currentUser.id },
365
428
  });
366
429
  if (!user) {
367
- throw new common_1.UnauthorizedException('User not found');
430
+ throw new common_1.UnauthorizedException({
431
+ message: 'User not found',
432
+ code: auth_constants_1.ERROR_CODES.USER_NOT_FOUND,
433
+ });
368
434
  }
369
435
  const isValid = await user.validatePassword(input.currentPassword);
370
436
  if (!isValid) {
371
- throw new common_1.BadRequestException('Current password is incorrect');
437
+ throw new common_1.BadRequestException({
438
+ message: 'Current password is incorrect',
439
+ code: auth_constants_1.ERROR_CODES.CURRENT_PASSWORD_INCORRECT,
440
+ });
372
441
  }
373
442
  if (input.currentPassword === input.newPassword) {
374
- throw new common_1.BadRequestException('New password must be different from the current password');
443
+ throw new common_1.BadRequestException({
444
+ message: 'New password must be different from the current password',
445
+ code: auth_constants_1.ERROR_CODES.NEW_PASSWORD_SAME_AS_CURRENT,
446
+ });
375
447
  }
376
448
  await user.setPassword(input.newPassword);
377
449
  await this.userRepository.save(user);
@@ -380,6 +452,10 @@ let AuthService = class AuthService {
380
452
  const session = await this.sessionManager.createSessionFromUser(hydratedUser);
381
453
  const tokens = await this.generateTokensFromSession(session);
382
454
  const isRequiresMfa = await this.mfaService.isRequiresMfa(user.id);
455
+ await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.PASSWORD_CHANGED, new user_password_changed_event_1.UserPasswordChangedEvent({
456
+ user,
457
+ initiatedBy: 'user'
458
+ }));
383
459
  this.debugLogger.logFunctionExit('changePassword', 'AuthService', { userId: user.id });
384
460
  return {
385
461
  accessToken: tokens.accessToken,
@@ -389,6 +465,7 @@ let AuthService = class AuthService {
389
465
  }
390
466
  catch (error) {
391
467
  this.debugLogger.logError(error, 'changePassword');
468
+ this.handleError(error, 'password_change');
392
469
  throw error;
393
470
  }
394
471
  }
@@ -407,17 +484,29 @@ let AuthService = class AuthService {
407
484
  provider = this.authProviderRegistry.getProvider(auth_constants_1.EMAIL_AUTH_PROVIDER);
408
485
  }
409
486
  else {
410
- throw new common_1.BadRequestException('Either email or phone must be provided');
487
+ throw new common_1.BadRequestException({
488
+ message: 'Either email or phone must be provided',
489
+ code: auth_constants_1.ERROR_CODES.EMAIL_OR_PHONE_REQUIRED,
490
+ });
411
491
  }
412
492
  if (!provider) {
413
- throw new common_1.BadRequestException('Phone or email authentication is not enabled');
493
+ throw new common_1.BadRequestException({
494
+ message: 'Phone or email authentication is not enabled',
495
+ code: auth_constants_1.ERROR_CODES.PROVIDER_NOT_FOUND,
496
+ });
414
497
  }
415
498
  if (!provider.enabled) {
416
499
  if (email) {
417
- throw new common_1.BadRequestException('Email authentication is not enabled');
500
+ throw new common_1.BadRequestException({
501
+ message: 'Email authentication is not enabled',
502
+ code: auth_constants_1.ERROR_CODES.PROVIDER_NOT_FOUND,
503
+ });
418
504
  }
419
505
  else if (phone) {
420
- throw new common_1.BadRequestException('Phone authentication is not enabled');
506
+ throw new common_1.BadRequestException({
507
+ message: 'Phone authentication is not enabled',
508
+ code: auth_constants_1.ERROR_CODES.PROVIDER_NOT_FOUND,
509
+ });
421
510
  }
422
511
  }
423
512
  const identity = await provider.findIdentity(email || phone);
@@ -449,6 +538,7 @@ let AuthService = class AuthService {
449
538
  }
450
539
  catch (error) {
451
540
  this.debugLogger.logError(error, 'forgotPassword', { email: input.email, phone: input.phone });
541
+ this.handleError(error, 'password_reset');
452
542
  throw error;
453
543
  }
454
544
  }
@@ -460,7 +550,10 @@ let AuthService = class AuthService {
460
550
  // Resolve tenant ID - use provided or default
461
551
  tenantId = await this.tenantService.resolveTenantId(tenantId);
462
552
  if (!email && !phone) {
463
- throw new common_1.BadRequestException('Either email or phone must be provided');
553
+ throw new common_1.BadRequestException({
554
+ message: 'Either email or phone must be provided',
555
+ code: auth_constants_1.ERROR_CODES.EMAIL_OR_PHONE_REQUIRED,
556
+ });
464
557
  }
465
558
  let provider = null;
466
559
  if (phone) {
@@ -470,11 +563,17 @@ let AuthService = class AuthService {
470
563
  provider = this.authProviderRegistry.getProvider(auth_constants_1.EMAIL_AUTH_PROVIDER);
471
564
  }
472
565
  if (!provider) {
473
- throw new common_1.BadRequestException('Phone or email authentication is not enabled');
566
+ throw new common_1.BadRequestException({
567
+ message: 'Phone or email authentication is not enabled',
568
+ code: auth_constants_1.ERROR_CODES.PROVIDER_NOT_FOUND,
569
+ });
474
570
  }
475
571
  const identity = await provider.findIdentity(email || phone);
476
572
  if (!identity) {
477
- throw new common_1.BadRequestException('Invalid reset request');
573
+ throw new common_1.BadRequestException({
574
+ message: 'Invalid reset request',
575
+ code: auth_constants_1.ERROR_CODES.PASSWORD_RESET_INVALID_REQUEST,
576
+ });
478
577
  }
479
578
  const validOtp = await this.otpRepository.findOne({
480
579
  where: {
@@ -486,10 +585,16 @@ let AuthService = class AuthService {
486
585
  relations: ['user']
487
586
  });
488
587
  if (!validOtp) {
489
- throw new common_1.BadRequestException('Invalid OTP code');
588
+ throw new common_1.BadRequestException({
589
+ message: 'Invalid OTP code',
590
+ code: auth_constants_1.ERROR_CODES.OTP_INVALID,
591
+ });
490
592
  }
491
593
  if ((0, moment_1.default)(validOtp.expiresAt).isBefore(new Date())) {
492
- throw new common_1.BadRequestException('OTP code expired');
594
+ throw new common_1.BadRequestException({
595
+ message: 'OTP code expired',
596
+ code: auth_constants_1.ERROR_CODES.OTP_EXPIRED,
597
+ });
493
598
  }
494
599
  const user = validOtp.user;
495
600
  // Generate JWT-based password reset token
@@ -510,6 +615,7 @@ let AuthService = class AuthService {
510
615
  }
511
616
  catch (error) {
512
617
  this.debugLogger.logError(error, 'verifyForgotPasswordOtp', { email: input.email, phone: input.phone });
618
+ this.handleError(error, 'password_reset');
513
619
  throw error;
514
620
  }
515
621
  }
@@ -521,7 +627,10 @@ let AuthService = class AuthService {
521
627
  // Resolve tenant ID - use provided or default
522
628
  tenantId = await this.tenantService.resolveTenantId(tenantId);
523
629
  if (!email && !phone) {
524
- throw new common_1.BadRequestException('Either email or phone must be provided');
630
+ throw new common_1.BadRequestException({
631
+ message: 'Either email or phone must be provided',
632
+ code: auth_constants_1.ERROR_CODES.EMAIL_OR_PHONE_REQUIRED,
633
+ });
525
634
  }
526
635
  // Find user by email or phone
527
636
  const user = await this.userRepository.findOne({
@@ -531,7 +640,10 @@ let AuthService = class AuthService {
531
640
  ]
532
641
  });
533
642
  if (!user) {
534
- throw new common_1.BadRequestException('Invalid reset request');
643
+ throw new common_1.BadRequestException({
644
+ message: 'Invalid reset request',
645
+ code: auth_constants_1.ERROR_CODES.PASSWORD_RESET_INVALID_REQUEST,
646
+ });
535
647
  }
536
648
  // Find valid OTP
537
649
  const validOtp = await this.otpRepository.findOne({
@@ -544,7 +656,10 @@ let AuthService = class AuthService {
544
656
  }
545
657
  });
546
658
  if (!validOtp) {
547
- throw new common_1.BadRequestException('Invalid or expired OTP');
659
+ throw new common_1.BadRequestException({
660
+ message: 'Invalid or expired OTP',
661
+ code: auth_constants_1.ERROR_CODES.OTP_INVALID,
662
+ });
548
663
  }
549
664
  // Update password
550
665
  await user.setPassword(newPassword);
@@ -563,6 +678,7 @@ let AuthService = class AuthService {
563
678
  }
564
679
  catch (error) {
565
680
  this.debugLogger.logError(error, 'resetPassword', { email: input.email, phone: input.phone });
681
+ this.handleError(error, 'password_reset');
566
682
  throw error;
567
683
  }
568
684
  }
@@ -576,23 +692,35 @@ let AuthService = class AuthService {
576
692
  decoded = await this.jwtService.verifyPasswordResetToken(token);
577
693
  }
578
694
  catch (error) {
579
- throw new common_1.BadRequestException('Invalid or expired reset token');
695
+ throw new common_1.BadRequestException({
696
+ message: 'Invalid or expired reset token',
697
+ code: auth_constants_1.ERROR_CODES.PASSWORD_RESET_TOKEN_INVALID,
698
+ });
580
699
  }
581
700
  if (decoded.type !== 'password-reset') {
582
- throw new common_1.BadRequestException('Invalid token type');
701
+ throw new common_1.BadRequestException({
702
+ message: 'Invalid token type',
703
+ code: auth_constants_1.ERROR_CODES.PASSWORD_RESET_TOKEN_INVALID,
704
+ });
583
705
  }
584
706
  // Get user
585
707
  const user = await this.userRepository.findOne({
586
708
  where: { id: decoded.userId }
587
709
  });
588
710
  if (!user) {
589
- throw new common_1.BadRequestException('User not found');
711
+ throw new common_1.BadRequestException({
712
+ message: 'User not found',
713
+ code: auth_constants_1.ERROR_CODES.USER_NOT_FOUND,
714
+ });
590
715
  }
591
716
  // Verify password hasn't changed since token was issued
592
717
  // This makes the token single-use in practice
593
718
  const currentPasswordHashPrefix = user.passwordHash ? user.passwordHash.substring(0, 10) : '';
594
719
  if (decoded.passwordHashPrefix !== currentPasswordHashPrefix) {
595
- throw new common_1.BadRequestException('Reset token is no longer valid');
720
+ throw new common_1.BadRequestException({
721
+ message: 'Reset token is no longer valid',
722
+ code: auth_constants_1.ERROR_CODES.PASSWORD_RESET_TOKEN_INVALID,
723
+ });
596
724
  }
597
725
  // Update password
598
726
  await user.setPassword(newPassword);
@@ -608,6 +736,7 @@ let AuthService = class AuthService {
608
736
  }
609
737
  catch (error) {
610
738
  this.debugLogger.logError(error, 'resetPasswordWithToken');
739
+ this.handleError(error, 'password_reset');
611
740
  throw error;
612
741
  }
613
742
  }
@@ -616,7 +745,7 @@ let AuthService = class AuthService {
616
745
  const user = await this.getUser();
617
746
  // Emit logout event
618
747
  await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.LOGGED_OUT, new logged_out_event_1.LoggedOutEvent({
619
- user,
748
+ user: user,
620
749
  tenantId: user?.tenantId,
621
750
  session,
622
751
  logoutType,
@@ -630,20 +759,25 @@ let AuthService = class AuthService {
630
759
  async logoutAll(userId, logoutType = 'user', reason) {
631
760
  const session = request_context_1.RequestContext.currentSession();
632
761
  if (!session) {
633
- throw new common_1.UnauthorizedException('Session not found');
762
+ throw new common_1.UnauthorizedException({
763
+ message: 'Session not found',
764
+ code: auth_constants_1.ERROR_CODES.SESSION_NOT_FOUND,
765
+ });
634
766
  }
635
767
  const sessions = await this.sessionManager.getUserSessions(userId);
636
768
  await this.sessionManager.revokeAllUserSessions(userId);
637
- const user = await this.getUser();
638
- // Emit logout event
639
- await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.LOGGED_OUT_ALL, new logged_out_all_event_1.LoggedOutAllEvent({
640
- user,
641
- tenantId: user?.tenantId,
642
- logoutType,
643
- reason,
644
- currentSessionId: session.id,
645
- sessions,
646
- }));
769
+ const user = await this.userRepository.findOne({ where: { id: userId } });
770
+ if (user) {
771
+ // Emit logout event
772
+ await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.LOGGED_OUT_ALL, new logged_out_all_event_1.LoggedOutAllEvent({
773
+ user,
774
+ tenantId: user.tenantId,
775
+ logoutType,
776
+ reason,
777
+ currentSessionId: session.id,
778
+ sessions,
779
+ }));
780
+ }
647
781
  return true;
648
782
  }
649
783
  async sendEmailVerification(input) {
@@ -651,14 +785,23 @@ let AuthService = class AuthService {
651
785
  try {
652
786
  const user = request_context_1.RequestContext.currentUser();
653
787
  if (!user) {
654
- throw new common_1.UnauthorizedException('User not authenticated');
788
+ throw new common_1.UnauthorizedException({
789
+ message: 'User not authenticated',
790
+ code: auth_constants_1.ERROR_CODES.UNAUTHORIZED,
791
+ });
655
792
  }
656
793
  const fullUser = await this.getUserWithRolesAndPermissions(user.id);
657
794
  if (!fullUser.email) {
658
- throw new common_1.BadRequestException('User does not have an email address');
795
+ throw new common_1.BadRequestException({
796
+ message: 'User does not have an email address',
797
+ code: auth_constants_1.ERROR_CODES.NO_EMAIL_ADDRESS,
798
+ });
659
799
  }
660
800
  if (fullUser.emailVerifiedAt) {
661
- throw new common_1.BadRequestException('Email is already verified');
801
+ throw new common_1.BadRequestException({
802
+ message: 'Email is already verified',
803
+ code: auth_constants_1.ERROR_CODES.EMAIL_ALREADY_VERIFIED,
804
+ });
662
805
  }
663
806
  // Generate OTP
664
807
  const otp = (0, otp_1.generateOtp)();
@@ -682,6 +825,7 @@ let AuthService = class AuthService {
682
825
  }
683
826
  catch (error) {
684
827
  this.debugLogger.logError(error, 'sendEmailVerification');
828
+ this.handleError(error, 'signup'); // Assuming email verification is part of signup flow or user profile management
685
829
  throw error;
686
830
  }
687
831
  }
@@ -690,14 +834,23 @@ let AuthService = class AuthService {
690
834
  try {
691
835
  const user = request_context_1.RequestContext.currentUser();
692
836
  if (!user) {
693
- throw new common_1.UnauthorizedException('User not authenticated');
837
+ throw new common_1.UnauthorizedException({
838
+ message: 'User not authenticated',
839
+ code: auth_constants_1.ERROR_CODES.UNAUTHORIZED,
840
+ });
694
841
  }
695
842
  const fullUser = await this.getUserWithRolesAndPermissions(user.id);
696
843
  if (!fullUser.email) {
697
- throw new common_1.BadRequestException('User does not have an email address');
844
+ throw new common_1.BadRequestException({
845
+ message: 'User does not have an email address',
846
+ code: auth_constants_1.ERROR_CODES.NO_EMAIL_ADDRESS,
847
+ });
698
848
  }
699
849
  if (fullUser.emailVerifiedAt) {
700
- throw new common_1.BadRequestException('Email is already verified');
850
+ throw new common_1.BadRequestException({
851
+ message: 'Email is already verified',
852
+ code: auth_constants_1.ERROR_CODES.EMAIL_ALREADY_VERIFIED,
853
+ });
701
854
  }
702
855
  // Find valid OTP
703
856
  const validOtp = await this.otpRepository.findOne({
@@ -709,10 +862,16 @@ let AuthService = class AuthService {
709
862
  }
710
863
  });
711
864
  if (!validOtp) {
712
- throw new common_1.BadRequestException('Invalid verification code');
865
+ throw new common_1.BadRequestException({
866
+ message: 'Invalid verification code',
867
+ code: auth_constants_1.ERROR_CODES.VERIFICATION_CODE_INVALID,
868
+ });
713
869
  }
714
870
  if ((0, moment_1.default)(validOtp.expiresAt).isBefore(new Date())) {
715
- throw new common_1.BadRequestException('Verification code has expired');
871
+ throw new common_1.BadRequestException({
872
+ message: 'Verification code has expired',
873
+ code: auth_constants_1.ERROR_CODES.VERIFICATION_CODE_EXPIRED,
874
+ });
716
875
  }
717
876
  // Mark OTP as used
718
877
  validOtp.used = true;
@@ -731,11 +890,12 @@ let AuthService = class AuthService {
731
890
  }
732
891
  catch (error) {
733
892
  this.debugLogger.logError(error, 'verifyEmail');
893
+ this.handleError(error, 'signup'); // Assuming email verification is part of signup flow or user profile management
734
894
  throw error;
735
895
  }
736
896
  }
737
- generateTokensPayload(session, otherPayload = {}) {
738
- const payload = {
897
+ async generateTokensPayload(session, otherPayload = {}) {
898
+ let payload = {
739
899
  id: session.userId,
740
900
  sub: session.userId,
741
901
  sessionId: session.id,
@@ -748,10 +908,29 @@ let AuthService = class AuthService {
748
908
  isMfaVerified: session.data?.isMfaVerified,
749
909
  ...otherPayload,
750
910
  };
911
+ // Apply custom token payload hook if configured
912
+ const config = this.authConfigService.getConfig();
913
+ if (config.session?.customizeTokenPayload) {
914
+ payload = await config.session.customizeTokenPayload(payload, session);
915
+ }
751
916
  return payload;
752
917
  }
918
+ /**
919
+ * Handle errors using the errorHandler hook if configured
920
+ */
921
+ handleError(error, context) {
922
+ const config = this.authConfigService.getConfig();
923
+ if (config.errorHandler) {
924
+ // The hook can throw a new error or return a modified one
925
+ // If it returns, we throw that. If it throws, it propagates.
926
+ const result = config.errorHandler(error, context);
927
+ if (result) {
928
+ throw result;
929
+ }
930
+ }
931
+ }
753
932
  async generateTokensFromSession(session) {
754
- const payload = this.generateTokensPayload(session);
933
+ const payload = await this.generateTokensPayload(session);
755
934
  const tokens = await this.jwtService.generateTokens(payload);
756
935
  return tokens;
757
936
  }
@@ -770,5 +949,6 @@ exports.AuthService = AuthService = tslib_1.__decorate([
770
949
  event_emitter_1.EventEmitter2,
771
950
  tenant_service_1.TenantService,
772
951
  debug_logger_service_1.DebugLoggerService,
773
- auth_config_service_1.AuthConfigService])
952
+ auth_config_service_1.AuthConfigService,
953
+ user_service_1.UserService])
774
954
  ], AuthService);