@flusys/nestjs-auth 9.1.2 → 9.2.1

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/fesm/24.js CHANGED
@@ -254,16 +254,39 @@ __webpack_require__.d(__webpack_exports__, {
254
254
 
255
255
 
256
256
  },
257
- 334(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
257
+ 37(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
258
+ /* import */ var _flusys_nestjs_shared_modules__rspack_import_0 = __webpack_require__(1236);
259
+ /* import */ var _flusys_nestjs_shared_utils__rspack_import_1 = __webpack_require__(9216);
260
+
261
+
262
+ const utils = new _flusys_nestjs_shared_modules__rspack_import_0.UtilsService();
263
+ /**
264
+ * Bumps the cache stamp of every entity a service wrote outside its own ApiService (repository,
265
+ * query runner, cascade), so every service caching those entities re-reads them. Call after commit.
266
+ */ async function invalidateEntityCaches(cache, dataSourceProvider, entityNames) {
267
+ if (!cache) return;
268
+ const tenantId = (0,_flusys_nestjs_shared_utils__rspack_import_1.cacheTenantId)(dataSourceProvider);
269
+ await Promise.all(entityNames.map((name)=>utils.clearCache(name, cache, tenantId)));
270
+ }
271
+
272
+ __webpack_require__.d(__webpack_exports__, {
273
+ i: () => (invalidateEntityCaches)
274
+ });
275
+
276
+
277
+ },
278
+ 5634(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
258
279
 
259
280
  // EXPORTS
260
281
  __webpack_require__.d(__webpack_exports__, {
261
- Vg: () => (/* reexport */ generateAccessToken),
262
- e: () => (/* reexport */ generateTokens),
282
+ Vg: () => (/* reexport */ token_helper/* .generateAccessToken */.Vg),
283
+ e: () => (/* reexport */ token_helper/* .generateTokens */.e),
284
+ PV: () => (/* reexport */ token_helper/* .getSessionsRevokedAtMs */.PV),
285
+ lL: () => (/* reexport */ token_helper/* .isIssuedBeforeRevocation */.lL),
263
286
  vR: () => (/* reexport */ branch_ancestry_helper/* .loadCompanyBranchNodes */.v),
264
287
  xu: () => (/* reexport */ branch_ancestry_helper/* .loadDisplayParentIds */.x),
265
- om: () => (/* reexport */ parseExpiryToMs),
266
- XP: () => (/* reexport */ revokeUserSessions),
288
+ om: () => (/* reexport */ token_helper/* .parseExpiryToMs */.om),
289
+ XP: () => (/* reexport */ token_helper/* .revokeUserSessions */.XP),
267
290
  QH: () => (/* reexport */ toBranchInfo),
268
291
  $C: () => (/* reexport */ toCompanyInfo),
269
292
  FV: () => (/* reexport */ toUserInfo)
@@ -298,14 +321,23 @@ function toBranchInfo(branch) {
298
321
 
299
322
  // EXTERNAL MODULE: ./projects/nestjs-auth/src/helpers/branch-ancestry.helper.ts
300
323
  var branch_ancestry_helper = __webpack_require__(7526);
301
- // EXTERNAL MODULE: ./projects/nestjs-auth/src/config/auth.constants.ts
302
- var auth_constants = __webpack_require__(5588);
303
- ;// CONCATENATED MODULE: ./projects/nestjs-auth/src/helpers/token.helper.ts
324
+ // EXTERNAL MODULE: ./projects/nestjs-auth/src/helpers/token.helper.ts
325
+ var token_helper = __webpack_require__(1379);
326
+ ;// CONCATENATED MODULE: ./projects/nestjs-auth/src/helpers/index.ts
327
+
328
+
329
+
330
+
304
331
 
305
- function generateAccessToken(jwtService, authConfig, payload) {
332
+ },
333
+ 1379(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
334
+ /* import */ var _config_auth_constants_js__rspack_import_0 = __webpack_require__(5588);
335
+
336
+ function generateAccessToken(jwtService, authConfig, payload, issuedAtMs = Date.now()) {
306
337
  return jwtService.sign({
307
338
  ...payload,
308
- type: 'access'
339
+ type: 'access',
340
+ iatMs: issuedAtMs
309
341
  }, {
310
342
  secret: authConfig.getJwtSecret(),
311
343
  expiresIn: authConfig.getJwtExpiration(),
@@ -313,10 +345,12 @@ function generateAccessToken(jwtService, authConfig, payload) {
313
345
  });
314
346
  }
315
347
  function generateTokens(jwtService, authConfig, payload) {
316
- const accessToken = generateAccessToken(jwtService, authConfig, payload);
348
+ const issuedAtMs = Date.now();
349
+ const accessToken = generateAccessToken(jwtService, authConfig, payload, issuedAtMs);
317
350
  const refreshToken = jwtService.sign({
318
351
  ...payload,
319
- type: 'refresh'
352
+ type: 'refresh',
353
+ iatMs: issuedAtMs
320
354
  }, {
321
355
  secret: authConfig.getRefreshTokenSecret(),
322
356
  expiresIn: authConfig.getRefreshTokenExpiration(),
@@ -345,15 +379,41 @@ function parseExpiryToMs(expiry) {
345
379
  return 7 * 24 * 60 * 60 * 1000;
346
380
  }
347
381
  }
382
+ // Markers written before millisecond precision hold epoch seconds
383
+ const LEGACY_SECONDS_LIMIT = 1e11;
384
+ function revocationKey(userId) {
385
+ return `${_config_auth_constants_js__rspack_import_0/* .TOKEN_REVOKED_PREFIX */.EV}:${userId}`;
386
+ }
387
+ /**
388
+ * When the user's sessions were last revoked, in epoch ms. Read past the in-process cache layer:
389
+ * the marker changes in place, and another instance's L1 copy would hold an older value.
390
+ */ async function getSessionsRevokedAtMs(cache, userId) {
391
+ const revokedAt = await cache.get(revocationKey(userId), {
392
+ skipMemory: true
393
+ });
394
+ if (!revokedAt) return undefined;
395
+ return revokedAt < LEGACY_SECONDS_LIMIT ? revokedAt * 1000 : revokedAt;
396
+ }
397
+ /** True when a token or session issued at `issuedAtMs` predates the user's last revocation */ async function isIssuedBeforeRevocation(cache, userId, issuedAtMs) {
398
+ const revokedAtMs = await getSessionsRevokedAtMs(cache, userId);
399
+ return revokedAtMs !== undefined && issuedAtMs < revokedAtMs;
400
+ }
348
401
  /** Rejects every token the user holds that was issued before now: checked on refresh and, by JwtStrategy, on every request. */ async function revokeUserSessions(cache, authConfig, userId) {
349
- const revokedAt = Math.floor(Date.now() / 1000);
350
- await cache.set(`${auth_constants/* .TOKEN_REVOKED_PREFIX */.EV}:${userId}`, revokedAt, parseExpiryToMs(authConfig.getRefreshTokenExpiration()));
402
+ // Never move the marker back (an instance whose clock lags would otherwise un-revoke tokens)
403
+ const revokedAtMs = Math.max(Date.now(), await getSessionsRevokedAtMs(cache, userId) ?? 0);
404
+ await cache.set(revocationKey(userId), revokedAtMs, parseExpiryToMs(authConfig.getRefreshTokenExpiration()), {
405
+ skipMemory: true
406
+ });
351
407
  }
352
408
 
353
- ;// CONCATENATED MODULE: ./projects/nestjs-auth/src/helpers/index.ts
354
-
355
-
356
-
409
+ __webpack_require__.d(__webpack_exports__, {
410
+ PV: () => (getSessionsRevokedAtMs),
411
+ Vg: () => (generateAccessToken),
412
+ XP: () => (revokeUserSessions),
413
+ e: () => (generateTokens),
414
+ lL: () => (isIssuedBeforeRevocation),
415
+ om: () => (parseExpiryToMs)
416
+ });
357
417
 
358
418
 
359
419
  },
@@ -639,6 +699,10 @@ const linkedInSocialProvider = new linkedin_social_provider_LinkedInSocialProvid
639
699
 
640
700
  const GRAPH_ME_URL = 'https://graph.microsoft.com/v1.0/me';
641
701
  const DEFAULT_TENANT = 'common';
702
+ const MULTI_TENANT_AUTHORITIES = new Set([
703
+ 'common',
704
+ 'organizations'
705
+ ]);
642
706
  function resolveTenant(credentials) {
643
707
  const tenantId = credentials.extraConfig?.['tenantId'];
644
708
  return typeof tenantId === 'string' && tenantId ? tenantId : DEFAULT_TENANT;
@@ -675,13 +739,14 @@ let microsoft_social_provider_MicrosoftSocialProvider = class MicrosoftSocialPro
675
739
  scope: resolveScopes(credentials, this.defaultScopes)
676
740
  });
677
741
  const profile = await getJson(this.provider, GRAPH_ME_URL, readAccessToken(this.provider, token));
678
- // `mail` is Microsoft's confirmed mailbox claim; `userPrincipalName` is a sign-in
679
- // identifier that is not guaranteed to be a verified, owned mailbox address.
680
- const verifiedEmail = readString(profile, 'mail');
742
+ // Any Entra tenant admin can set `mail` to an address they do not own, so it only counts as
743
+ // verified when sign-in is pinned to one tenant (or to personal accounts), never common/organizations.
744
+ const mail = readString(profile, 'mail');
745
+ const trustsMail = !MULTI_TENANT_AUTHORITIES.has(resolveTenant(credentials).toLowerCase());
681
746
  return {
682
747
  providerUserId: readString(profile, 'id') ?? '',
683
- email: verifiedEmail ?? readString(profile, 'userPrincipalName'),
684
- emailVerified: !!verifiedEmail,
748
+ email: mail ?? readString(profile, 'userPrincipalName'),
749
+ emailVerified: !!mail && trustsMail,
685
750
  name: readString(profile, 'displayName'),
686
751
  avatarUrl: null
687
752
  };
@@ -925,13 +990,17 @@ __webpack_require__.d(__webpack_exports__, {
925
990
  /* import */ var _flusys_nestjs_shared_utils__rspack_import_3 = __webpack_require__(9216);
926
991
  /* import */ var _nestjs_common__rspack_import_4 = __webpack_require__(6157);
927
992
  /* import */ var bcrypt__rspack_import_5 = __webpack_require__(7176);
928
- /* import */ var _config_index_js__rspack_import_10 = __webpack_require__(5363);
929
- /* import */ var _config_index_js__rspack_import_12 = __webpack_require__(7987);
930
- /* import */ var _entities_app_user_entity_js__rspack_import_6 = __webpack_require__(4758);
931
- /* import */ var _entities_pending_registration_entity_js__rspack_import_7 = __webpack_require__(9388);
932
- /* import */ var _entities_user_token_entity_js__rspack_import_8 = __webpack_require__(9696);
933
- /* import */ var _interfaces_index_js__rspack_import_13 = __webpack_require__(9125);
934
- /* import */ var _auth_datasource_provider_js__rspack_import_9 = __webpack_require__(2732);
993
+ /* import */ var typeorm__rspack_import_6 = __webpack_require__(7158);
994
+ /* import */ var _config_index_js__rspack_import_13 = __webpack_require__(5363);
995
+ /* import */ var _config_index_js__rspack_import_15 = __webpack_require__(7987);
996
+ /* import */ var _entities_app_user_entity_js__rspack_import_7 = __webpack_require__(4758);
997
+ /* import */ var _entities_pending_registration_entity_js__rspack_import_8 = __webpack_require__(9388);
998
+ /* import */ var _entities_user_token_entity_js__rspack_import_9 = __webpack_require__(9696);
999
+ /* import */ var _helpers_cache_invalidation_helper_js__rspack_import_10 = __webpack_require__(37);
1000
+ /* import */ var _helpers_token_helper_js__rspack_import_16 = __webpack_require__(1379);
1001
+ /* import */ var _interfaces_index_js__rspack_import_17 = __webpack_require__(9125);
1002
+ /* import */ var _auth_config_service_js__rspack_import_11 = __webpack_require__(6514);
1003
+ /* import */ var _auth_datasource_provider_js__rspack_import_12 = __webpack_require__(2732);
935
1004
  function _ts_decorate(decorators, target, key, desc) {
936
1005
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
937
1006
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -967,14 +1036,23 @@ function _ts_param(paramIndex, decorator) {
967
1036
 
968
1037
 
969
1038
 
1039
+
1040
+
1041
+
1042
+
970
1043
  class AuthEmailService {
971
1044
  dataSourceProvider;
972
1045
  emailProvider;
1046
+ cacheManager;
1047
+ authConfig;
973
1048
  PASSWORD_RESET_EXPIRY = 60 * 60 * 1000;
974
1049
  EMAIL_VERIFICATION_EXPIRY = 24 * 60 * 60 * 1000;
975
- constructor(dataSourceProvider, emailProvider){
1050
+ logger = (0,_flusys_nestjs_shared_classes__rspack_import_1.createModuleLogger)('auth');
1051
+ constructor(dataSourceProvider, emailProvider, cacheManager, authConfig){
976
1052
  this.dataSourceProvider = dataSourceProvider;
977
1053
  this.emailProvider = emailProvider;
1054
+ this.cacheManager = cacheManager;
1055
+ this.authConfig = authConfig;
978
1056
  }
979
1057
  isEmailEnabled() {
980
1058
  return !!this.emailProvider;
@@ -1001,7 +1079,7 @@ class AuthEmailService {
1001
1079
  if (!this.emailProvider) {
1002
1080
  throw new _nestjs_common__rspack_import_4.BadRequestException({
1003
1081
  message: 'Email service is not configured',
1004
- messageKey: _config_index_js__rspack_import_10/* .AUTH_MESSAGES.EMAIL_NOT_CONFIGURED */.mK.EMAIL_NOT_CONFIGURED
1082
+ messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.EMAIL_NOT_CONFIGURED */.mK.EMAIL_NOT_CONFIGURED
1005
1083
  });
1006
1084
  }
1007
1085
  }
@@ -1011,7 +1089,7 @@ class AuthEmailService {
1011
1089
  } catch {
1012
1090
  throw new _nestjs_common__rspack_import_4.BadRequestException({
1013
1091
  message: 'Failed to send verification email. Please try again.',
1014
- messageKey: _config_index_js__rspack_import_10/* .AUTH_MESSAGES.EMAIL_SEND_FAILED */.mK.EMAIL_SEND_FAILED
1092
+ messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.EMAIL_SEND_FAILED */.mK.EMAIL_SEND_FAILED
1015
1093
  });
1016
1094
  }
1017
1095
  }
@@ -1029,20 +1107,21 @@ class AuthEmailService {
1029
1107
  });
1030
1108
  }
1031
1109
  }
1032
- async hasPendingRegistration(email) {
1110
+ async hasPendingRegistration(email, password) {
1033
1111
  if (!this.isEmailEnabled()) return false;
1034
- const pendingRepository = await this.getRepository(_entities_pending_registration_entity_js__rspack_import_7/* .PendingRegistration */.N);
1112
+ const pendingRepository = await this.getRepository(_entities_pending_registration_entity_js__rspack_import_8/* .PendingRegistration */.N);
1035
1113
  const pending = await pendingRepository.findOne({
1036
1114
  where: {
1037
1115
  email
1038
1116
  }
1039
1117
  });
1040
- return !!pending && !(0,_flusys_nestjs_shared_utils__rspack_import_3.isExpired)(pending.expiresAt);
1118
+ if (!pending || (0,_flusys_nestjs_shared_utils__rspack_import_3.isExpired)(pending.expiresAt)) return false;
1119
+ return password === undefined || await bcrypt__rspack_import_5.compare(password, pending.passwordHash);
1041
1120
  }
1042
1121
  async createPendingRegistration(data) {
1043
1122
  this.assertEmailEnabled();
1044
- const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_6/* .AppUser */.$);
1045
- const pendingRepository = await this.getRepository(_entities_pending_registration_entity_js__rspack_import_7/* .PendingRegistration */.N);
1123
+ const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_7/* .AppUser */.$);
1124
+ const pendingRepository = await this.getRepository(_entities_pending_registration_entity_js__rspack_import_8/* .PendingRegistration */.N);
1046
1125
  // Check if user already exists
1047
1126
  const existingUser = await userRepository.findOne({
1048
1127
  where: {
@@ -1052,7 +1131,7 @@ class AuthEmailService {
1052
1131
  if (existingUser) {
1053
1132
  throw new _nestjs_common__rspack_import_4.BadRequestException({
1054
1133
  message: 'Registration failed. Please try again or contact support.',
1055
- messageKey: _config_index_js__rspack_import_10/* .AUTH_MESSAGES.REGISTER_FAILED */.mK.REGISTER_FAILED
1134
+ messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.REGISTER_FAILED */.mK.REGISTER_FAILED
1056
1135
  });
1057
1136
  }
1058
1137
  // Delete any existing pending registration for this email
@@ -1081,13 +1160,13 @@ class AuthEmailService {
1081
1160
  return {
1082
1161
  success: true,
1083
1162
  message: 'Please check your email to verify your registration.',
1084
- messageKey: _config_index_js__rspack_import_10/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_SENT */.sv.VERIFICATION_SENT
1163
+ messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_SENT */.sv.VERIFICATION_SENT
1085
1164
  };
1086
1165
  }
1087
1166
  async forgotPassword(email, resetUrlBase) {
1088
1167
  this.assertEmailEnabled();
1089
- const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_6/* .AppUser */.$);
1090
- const tokenRepository = await this.getRepository(_entities_user_token_entity_js__rspack_import_8/* .UserToken */.L);
1168
+ const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_7/* .AppUser */.$);
1169
+ const tokenRepository = await this.getRepository(_entities_user_token_entity_js__rspack_import_9/* .UserToken */.L);
1091
1170
  const user = await userRepository.findOne({
1092
1171
  where: {
1093
1172
  email
@@ -1098,20 +1177,20 @@ class AuthEmailService {
1098
1177
  return {
1099
1178
  success: true,
1100
1179
  message: 'If the email exists, a password reset link has been sent.',
1101
- messageKey: _config_index_js__rspack_import_10/* .AUTH_MESSAGES.PASSWORD_RESET_EMAIL_SENT */.mK.PASSWORD_RESET_EMAIL_SENT
1180
+ messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_RESET_EMAIL_SENT */.mK.PASSWORD_RESET_EMAIL_SENT
1102
1181
  };
1103
1182
  }
1104
1183
  // Invalidate existing password reset tokens for this user
1105
1184
  await tokenRepository.delete({
1106
1185
  userId: user.id,
1107
- type: _entities_user_token_entity_js__rspack_import_8/* .TokenType.PASSWORD_RESET */.k.PASSWORD_RESET
1186
+ type: _entities_user_token_entity_js__rspack_import_9/* .TokenType.PASSWORD_RESET */.k.PASSWORD_RESET
1108
1187
  });
1109
1188
  // Create new token with hashed storage
1110
1189
  const token = this.generateToken();
1111
1190
  const tokenHash = this.hashToken(token);
1112
1191
  const userToken = tokenRepository.create({
1113
1192
  userId: user.id,
1114
- type: _entities_user_token_entity_js__rspack_import_8/* .TokenType.PASSWORD_RESET */.k.PASSWORD_RESET,
1193
+ type: _entities_user_token_entity_js__rspack_import_9/* .TokenType.PASSWORD_RESET */.k.PASSWORD_RESET,
1115
1194
  token: tokenHash,
1116
1195
  expiresAt: new Date(Date.now() + this.PASSWORD_RESET_EXPIRY)
1117
1196
  });
@@ -1120,43 +1199,36 @@ class AuthEmailService {
1120
1199
  const resetUrl = `${resetUrlBase}?token=${token}`;
1121
1200
  try {
1122
1201
  await this.emailProvider.sendPasswordResetEmail(email, token, resetUrl);
1123
- } catch {
1124
- throw new _nestjs_common__rspack_import_4.BadRequestException({
1125
- message: 'Failed to process request. Please try again later.',
1126
- messageKey: _flusys_nestjs_shared_constants__rspack_import_2.SYSTEM_MESSAGES.INTERNAL_ERROR,
1127
- messageVariables: {
1128
- provider: 'EmailProvider',
1129
- error: 'Send email failed'
1130
- }
1131
- });
1202
+ } catch (error) {
1203
+ this.logger.error(`Password reset email could not be sent: ${error instanceof Error ? error.message : String(error)}`);
1132
1204
  }
1133
- await this.publishUserEvent(_config_index_js__rspack_import_12/* .AUTH_EVENT_ACTIONS.PASSWORD_RESET_REQUESTED */.o.PASSWORD_RESET_REQUESTED, user.id, {
1205
+ await this.publishUserEvent(_config_index_js__rspack_import_15/* .AUTH_EVENT_ACTIONS.PASSWORD_RESET_REQUESTED */.o.PASSWORD_RESET_REQUESTED, user.id, {
1134
1206
  email: user.email
1135
1207
  });
1136
1208
  return {
1137
1209
  success: true,
1138
1210
  message: 'If the email exists, a password reset link has been sent.',
1139
- messageKey: _config_index_js__rspack_import_10/* .AUTH_MESSAGES.PASSWORD_RESET_EMAIL_SENT */.mK.PASSWORD_RESET_EMAIL_SENT
1211
+ messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_RESET_EMAIL_SENT */.mK.PASSWORD_RESET_EMAIL_SENT
1140
1212
  };
1141
1213
  }
1142
1214
  async resetPassword(token, newPassword) {
1143
- const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_6/* .AppUser */.$);
1144
- const tokenRepository = await this.getRepository(_entities_user_token_entity_js__rspack_import_8/* .UserToken */.L);
1215
+ const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_7/* .AppUser */.$);
1216
+ const tokenRepository = await this.getRepository(_entities_user_token_entity_js__rspack_import_9/* .UserToken */.L);
1145
1217
  // Hash the incoming token to compare with stored hash
1146
1218
  const tokenHash = this.hashToken(token);
1147
1219
  const userToken = await tokenRepository.findOne({
1148
1220
  where: {
1149
1221
  token: tokenHash,
1150
- type: _entities_user_token_entity_js__rspack_import_8/* .TokenType.PASSWORD_RESET */.k.PASSWORD_RESET
1222
+ type: _entities_user_token_entity_js__rspack_import_9/* .TokenType.PASSWORD_RESET */.k.PASSWORD_RESET
1151
1223
  }
1152
1224
  });
1153
1225
  if (!userToken) {
1154
1226
  throw new _nestjs_common__rspack_import_4.BadRequestException({
1155
1227
  message: 'Invalid or expired reset token',
1156
- messageKey: _config_index_js__rspack_import_10/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_INVALID */.sv.VERIFICATION_TOKEN_INVALID
1228
+ messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_INVALID */.sv.VERIFICATION_TOKEN_INVALID
1157
1229
  });
1158
1230
  }
1159
- this.validateTokenNotExpiredOrUsed(userToken, 'Reset token has expired. Please request a new one.', _config_index_js__rspack_import_10/* .AUTH_MESSAGES.PASSWORD_RESET_TOKEN_EXPIRED */.mK.PASSWORD_RESET_TOKEN_EXPIRED, 'This reset token has already been used.', _config_index_js__rspack_import_10/* .AUTH_MESSAGES.PASSWORD_RESET_TOKEN_USED */.mK.PASSWORD_RESET_TOKEN_USED);
1231
+ this.validateTokenNotExpiredOrUsed(userToken, 'Reset token has expired. Please request a new one.', _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_RESET_TOKEN_EXPIRED */.mK.PASSWORD_RESET_TOKEN_EXPIRED, 'This reset token has already been used.', _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_RESET_TOKEN_USED */.mK.PASSWORD_RESET_TOKEN_USED);
1160
1232
  const user = await userRepository.findOne({
1161
1233
  where: {
1162
1234
  id: userToken.userId
@@ -1165,28 +1237,42 @@ class AuthEmailService {
1165
1237
  if (!user) {
1166
1238
  throw new _nestjs_common__rspack_import_4.BadRequestException({
1167
1239
  message: 'User not found',
1168
- messageKey: _config_index_js__rspack_import_10/* .USER_MESSAGES.NOT_FOUND */.ny.NOT_FOUND
1240
+ messageKey: _config_index_js__rspack_import_13/* .USER_MESSAGES.NOT_FOUND */.ny.NOT_FOUND
1241
+ });
1242
+ }
1243
+ const claimed = await tokenRepository.update({
1244
+ id: userToken.id,
1245
+ usedAt: (0,typeorm__rspack_import_6.IsNull)()
1246
+ }, {
1247
+ usedAt: new Date()
1248
+ });
1249
+ if (claimed?.affected === 0) {
1250
+ throw new _nestjs_common__rspack_import_4.BadRequestException({
1251
+ message: 'This reset token has already been used.',
1252
+ messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_RESET_TOKEN_USED */.mK.PASSWORD_RESET_TOKEN_USED
1169
1253
  });
1170
1254
  }
1171
- // Update password
1172
1255
  user.password = await bcrypt__rspack_import_5.hash(newPassword, (/* inlined export .BCRYPT_SALT_ROUNDS */12));
1173
1256
  await userRepository.save(user);
1174
- // Mark token as used
1175
- userToken.usedAt = new Date();
1176
- await tokenRepository.save(userToken);
1177
- await this.publishUserEvent(_config_index_js__rspack_import_12/* .AUTH_EVENT_ACTIONS.PASSWORD_RESET */.o.PASSWORD_RESET, user.id, {
1257
+ await (0,_helpers_cache_invalidation_helper_js__rspack_import_10/* .invalidateEntityCaches */.i)(this.cacheManager, this.dataSourceProvider, [
1258
+ _config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.USER */.g.USER
1259
+ ]);
1260
+ if (this.cacheManager && this.authConfig) {
1261
+ await (0,_helpers_token_helper_js__rspack_import_16/* .revokeUserSessions */.XP)(this.cacheManager, this.authConfig, user.id);
1262
+ }
1263
+ await this.publishUserEvent(_config_index_js__rspack_import_15/* .AUTH_EVENT_ACTIONS.PASSWORD_RESET */.o.PASSWORD_RESET, user.id, {
1178
1264
  email: user.email
1179
1265
  });
1180
1266
  return {
1181
1267
  success: true,
1182
1268
  message: 'Password has been reset successfully.',
1183
- messageKey: _config_index_js__rspack_import_10/* .AUTH_MESSAGES.PASSWORD_RESET_SUCCESS */.mK.PASSWORD_RESET_SUCCESS
1269
+ messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_RESET_SUCCESS */.mK.PASSWORD_RESET_SUCCESS
1184
1270
  };
1185
1271
  }
1186
1272
  async verifyEmail(token) {
1187
- const pendingRepository = await this.getRepository(_entities_pending_registration_entity_js__rspack_import_7/* .PendingRegistration */.N);
1188
- const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_6/* .AppUser */.$);
1189
- const tokenRepository = await this.getRepository(_entities_user_token_entity_js__rspack_import_8/* .UserToken */.L);
1273
+ const pendingRepository = await this.getRepository(_entities_pending_registration_entity_js__rspack_import_8/* .PendingRegistration */.N);
1274
+ const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_7/* .AppUser */.$);
1275
+ const tokenRepository = await this.getRepository(_entities_user_token_entity_js__rspack_import_9/* .UserToken */.L);
1190
1276
  // Hash the incoming token to compare with stored hash
1191
1277
  const tokenHash = this.hashToken(token);
1192
1278
  // Check for pending registration first
@@ -1202,13 +1288,18 @@ class AuthEmailService {
1202
1288
  });
1203
1289
  throw new _nestjs_common__rspack_import_4.BadRequestException({
1204
1290
  message: 'Verification token has expired. Please register again.',
1205
- messageKey: _config_index_js__rspack_import_10/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_EXPIRED */.sv.VERIFICATION_TOKEN_EXPIRED
1291
+ messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_EXPIRED */.sv.VERIFICATION_TOKEN_EXPIRED
1206
1292
  });
1207
1293
  }
1208
- // Delete pending registration (will be consumed by caller to create user)
1209
- await pendingRepository.delete({
1294
+ const consumed = await pendingRepository.delete({
1210
1295
  id: pending.id
1211
1296
  });
1297
+ if (consumed?.affected === 0) {
1298
+ throw new _nestjs_common__rspack_import_4.BadRequestException({
1299
+ message: 'Invalid or expired verification token',
1300
+ messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_INVALID */.sv.VERIFICATION_TOKEN_INVALID
1301
+ });
1302
+ }
1212
1303
  return {
1213
1304
  type: 'pending',
1214
1305
  pending
@@ -1218,13 +1309,13 @@ class AuthEmailService {
1218
1309
  const userToken = await tokenRepository.findOne({
1219
1310
  where: {
1220
1311
  token: tokenHash,
1221
- type: _entities_user_token_entity_js__rspack_import_8/* .TokenType.EMAIL_VERIFICATION */.k.EMAIL_VERIFICATION
1312
+ type: _entities_user_token_entity_js__rspack_import_9/* .TokenType.EMAIL_VERIFICATION */.k.EMAIL_VERIFICATION
1222
1313
  }
1223
1314
  });
1224
1315
  if (!userToken) {
1225
1316
  throw new _nestjs_common__rspack_import_4.BadRequestException({
1226
1317
  message: 'Invalid or expired verification token',
1227
- messageKey: _config_index_js__rspack_import_10/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_INVALID */.sv.VERIFICATION_TOKEN_INVALID
1318
+ messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_INVALID */.sv.VERIFICATION_TOKEN_INVALID
1228
1319
  });
1229
1320
  }
1230
1321
  if ((0,_flusys_nestjs_shared_utils__rspack_import_3.isExpired)(userToken.expiresAt)) {
@@ -1233,14 +1324,14 @@ class AuthEmailService {
1233
1324
  });
1234
1325
  throw new _nestjs_common__rspack_import_4.BadRequestException({
1235
1326
  message: 'Verification token has expired. Please request a new one.',
1236
- messageKey: _config_index_js__rspack_import_10/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_EXPIRED */.sv.VERIFICATION_TOKEN_EXPIRED
1327
+ messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_EXPIRED */.sv.VERIFICATION_TOKEN_EXPIRED
1237
1328
  });
1238
1329
  }
1239
1330
  if (userToken.usedAt) {
1240
1331
  return {
1241
1332
  type: 'verified',
1242
1333
  message: 'Email has already been verified.',
1243
- messageKey: _config_index_js__rspack_import_10/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_SUCCESS */.sv.VERIFICATION_SUCCESS
1334
+ messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_SUCCESS */.sv.VERIFICATION_SUCCESS
1244
1335
  };
1245
1336
  }
1246
1337
  const user = await userRepository.findOne({
@@ -1251,28 +1342,31 @@ class AuthEmailService {
1251
1342
  if (!user) {
1252
1343
  throw new _nestjs_common__rspack_import_4.BadRequestException({
1253
1344
  message: 'User not found',
1254
- messageKey: _config_index_js__rspack_import_10/* .USER_MESSAGES.NOT_FOUND */.ny.NOT_FOUND
1345
+ messageKey: _config_index_js__rspack_import_13/* .USER_MESSAGES.NOT_FOUND */.ny.NOT_FOUND
1255
1346
  });
1256
1347
  }
1257
1348
  // Mark email as verified
1258
1349
  user.emailVerified = true;
1259
1350
  await userRepository.save(user);
1351
+ await (0,_helpers_cache_invalidation_helper_js__rspack_import_10/* .invalidateEntityCaches */.i)(this.cacheManager, this.dataSourceProvider, [
1352
+ _config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.USER */.g.USER
1353
+ ]);
1260
1354
  // Mark token as used
1261
1355
  userToken.usedAt = new Date();
1262
1356
  await tokenRepository.save(userToken);
1263
- await this.publishUserEvent(_config_index_js__rspack_import_12/* .AUTH_EVENT_ACTIONS.EMAIL_VERIFIED */.o.EMAIL_VERIFIED, user.id, {
1357
+ await this.publishUserEvent(_config_index_js__rspack_import_15/* .AUTH_EVENT_ACTIONS.EMAIL_VERIFIED */.o.EMAIL_VERIFIED, user.id, {
1264
1358
  email: user.email
1265
1359
  });
1266
1360
  return {
1267
1361
  type: 'verified',
1268
1362
  message: 'Email has been verified successfully.',
1269
- messageKey: _config_index_js__rspack_import_10/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_SUCCESS */.sv.VERIFICATION_SUCCESS
1363
+ messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_SUCCESS */.sv.VERIFICATION_SUCCESS
1270
1364
  };
1271
1365
  }
1272
1366
  async publishUserEvent(action, userId, metadata) {
1273
1367
  await (0,_flusys_nestjs_shared_classes__rspack_import_1.publishDomainEvent)({
1274
1368
  module: (/* inlined export .AUTH_EVENT_MODULE */"auth"),
1275
- entity: _config_index_js__rspack_import_12/* .AUTH_EVENT_ENTITIES.USER */.g.USER,
1369
+ entity: _config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.USER */.g.USER,
1276
1370
  action,
1277
1371
  ids: [
1278
1372
  userId
@@ -1282,9 +1376,9 @@ class AuthEmailService {
1282
1376
  }
1283
1377
  async resendVerificationEmail(email, verifyUrlBase) {
1284
1378
  this.assertEmailEnabled();
1285
- const pendingRepository = await this.getRepository(_entities_pending_registration_entity_js__rspack_import_7/* .PendingRegistration */.N);
1286
- const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_6/* .AppUser */.$);
1287
- const tokenRepository = await this.getRepository(_entities_user_token_entity_js__rspack_import_8/* .UserToken */.L);
1379
+ const pendingRepository = await this.getRepository(_entities_pending_registration_entity_js__rspack_import_8/* .PendingRegistration */.N);
1380
+ const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_7/* .AppUser */.$);
1381
+ const tokenRepository = await this.getRepository(_entities_user_token_entity_js__rspack_import_9/* .UserToken */.L);
1288
1382
  // Check pending registration first
1289
1383
  const pending = await pendingRepository.findOne({
1290
1384
  where: {
@@ -1301,7 +1395,7 @@ class AuthEmailService {
1301
1395
  return {
1302
1396
  success: true,
1303
1397
  message: 'If the email requires verification, a link has been sent.',
1304
- messageKey: _config_index_js__rspack_import_10/* .EMAIL_VERIFICATION_MESSAGES.RESEND_SUCCESS */.sv.RESEND_SUCCESS
1398
+ messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.RESEND_SUCCESS */.sv.RESEND_SUCCESS
1305
1399
  };
1306
1400
  }
1307
1401
  // Check existing user
@@ -1314,18 +1408,18 @@ class AuthEmailService {
1314
1408
  return {
1315
1409
  success: true,
1316
1410
  message: 'If the email requires verification, a link has been sent.',
1317
- messageKey: _config_index_js__rspack_import_10/* .EMAIL_VERIFICATION_MESSAGES.RESEND_SUCCESS */.sv.RESEND_SUCCESS
1411
+ messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.RESEND_SUCCESS */.sv.RESEND_SUCCESS
1318
1412
  };
1319
1413
  }
1320
1414
  // Invalidate existing tokens and create new one
1321
1415
  await tokenRepository.delete({
1322
1416
  userId: user.id,
1323
- type: _entities_user_token_entity_js__rspack_import_8/* .TokenType.EMAIL_VERIFICATION */.k.EMAIL_VERIFICATION
1417
+ type: _entities_user_token_entity_js__rspack_import_9/* .TokenType.EMAIL_VERIFICATION */.k.EMAIL_VERIFICATION
1324
1418
  });
1325
1419
  const token = this.generateToken();
1326
1420
  const userToken = tokenRepository.create({
1327
1421
  userId: user.id,
1328
- type: _entities_user_token_entity_js__rspack_import_8/* .TokenType.EMAIL_VERIFICATION */.k.EMAIL_VERIFICATION,
1422
+ type: _entities_user_token_entity_js__rspack_import_9/* .TokenType.EMAIL_VERIFICATION */.k.EMAIL_VERIFICATION,
1329
1423
  token: this.hashToken(token),
1330
1424
  expiresAt: new Date(Date.now() + this.EMAIL_VERIFICATION_EXPIRY)
1331
1425
  });
@@ -1335,7 +1429,7 @@ class AuthEmailService {
1335
1429
  return {
1336
1430
  success: true,
1337
1431
  message: 'If the email requires verification, a link has been sent.',
1338
- messageKey: _config_index_js__rspack_import_10/* .EMAIL_VERIFICATION_MESSAGES.RESEND_SUCCESS */.sv.RESEND_SUCCESS
1432
+ messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.RESEND_SUCCESS */.sv.RESEND_SUCCESS
1339
1433
  };
1340
1434
  }
1341
1435
  }
@@ -1405,13 +1499,19 @@ AuthEmailService = _ts_decorate([
1405
1499
  scope: _nestjs_common__rspack_import_4.Scope.REQUEST
1406
1500
  }),
1407
1501
  _ts_param(0, (0,_nestjs_common__rspack_import_4.Optional)()),
1408
- _ts_param(0, (0,_nestjs_common__rspack_import_4.Inject)(_auth_datasource_provider_js__rspack_import_9/* .AuthDataSourceProvider */.v)),
1502
+ _ts_param(0, (0,_nestjs_common__rspack_import_4.Inject)(_auth_datasource_provider_js__rspack_import_12/* .AuthDataSourceProvider */.v)),
1409
1503
  _ts_param(1, (0,_nestjs_common__rspack_import_4.Optional)()),
1410
- _ts_param(1, (0,_nestjs_common__rspack_import_4.Inject)(_interfaces_index_js__rspack_import_13/* .AUTH_EMAIL_PROVIDER */.C)),
1504
+ _ts_param(1, (0,_nestjs_common__rspack_import_4.Inject)(_interfaces_index_js__rspack_import_17/* .AUTH_EMAIL_PROVIDER */.C)),
1505
+ _ts_param(2, (0,_nestjs_common__rspack_import_4.Optional)()),
1506
+ _ts_param(2, (0,_nestjs_common__rspack_import_4.Inject)('CACHE_INSTANCE')),
1507
+ _ts_param(3, (0,_nestjs_common__rspack_import_4.Optional)()),
1508
+ _ts_param(3, (0,_nestjs_common__rspack_import_4.Inject)(_auth_config_service_js__rspack_import_11/* .AuthConfigService */.W)),
1411
1509
  _ts_metadata("design:type", Function),
1412
1510
  _ts_metadata("design:paramtypes", [
1413
- typeof _auth_datasource_provider_js__rspack_import_9/* .AuthDataSourceProvider */.v === "undefined" ? Object : _auth_datasource_provider_js__rspack_import_9/* .AuthDataSourceProvider */.v,
1414
- typeof IAuthEmailProvider === "undefined" ? Object : IAuthEmailProvider
1511
+ typeof _auth_datasource_provider_js__rspack_import_12/* .AuthDataSourceProvider */.v === "undefined" ? Object : _auth_datasource_provider_js__rspack_import_12/* .AuthDataSourceProvider */.v,
1512
+ typeof IAuthEmailProvider === "undefined" ? Object : IAuthEmailProvider,
1513
+ typeof _flusys_nestjs_shared_classes__rspack_import_1.HybridCache === "undefined" ? Object : _flusys_nestjs_shared_classes__rspack_import_1.HybridCache,
1514
+ typeof _auth_config_service_js__rspack_import_11/* .AuthConfigService */.W === "undefined" ? Object : _auth_config_service_js__rspack_import_11/* .AuthConfigService */.W
1415
1515
  ])
1416
1516
  ], AuthEmailService);
1417
1517
 
@@ -1466,8 +1566,10 @@ var pending_registration_entity = __webpack_require__(9388);
1466
1566
  var user_company_permission_entity = __webpack_require__(2377);
1467
1567
  // EXTERNAL MODULE: ./projects/nestjs-auth/src/enums/user-org-permission-type.enum.ts
1468
1568
  var user_org_permission_type_enum = __webpack_require__(5081);
1469
- // EXTERNAL MODULE: ./projects/nestjs-auth/src/helpers/index.ts + 2 modules
1470
- var helpers = __webpack_require__(334);
1569
+ // EXTERNAL MODULE: ./projects/nestjs-auth/src/helpers/index.ts + 1 modules
1570
+ var helpers = __webpack_require__(5634);
1571
+ // EXTERNAL MODULE: ./projects/nestjs-auth/src/helpers/cache-invalidation.helper.ts
1572
+ var cache_invalidation_helper = __webpack_require__(37);
1471
1573
  // EXTERNAL MODULE: ./projects/nestjs-auth/src/services/auth-config.service.ts + 1 modules
1472
1574
  var auth_config_service = __webpack_require__(6514);
1473
1575
  // EXTERNAL MODULE: ./projects/nestjs-auth/src/services/auth-datasource.provider.ts
@@ -1522,6 +1624,8 @@ function _ts_param(paramIndex, decorator) {
1522
1624
 
1523
1625
 
1524
1626
 
1627
+
1628
+ const TIMING_SAFE_DUMMY_HASH = '$2b$12$1l4HGUUGpuL.DdwxrAHDmeUINEEDQVBT4lPvdSYEQDNm5BFNskVum';
1525
1629
  class AuthenticationService {
1526
1630
  jwtService;
1527
1631
  authConfig;
@@ -1569,8 +1673,11 @@ class AuthenticationService {
1569
1673
  if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
1570
1674
  return this.dataSourceProvider.getRepository(entity);
1571
1675
  }
1676
+ /** This service writes users, companies, branches and grants directly; their cached reads go stale */ async invalidateCaches(...entityNames) {
1677
+ await (0,cache_invalidation_helper/* .invalidateEntityCaches */.i)(this.cacheManager, this.dataSourceProvider, entityNames);
1678
+ }
1572
1679
  async login(email, password) {
1573
- if (this.isEmailVerificationEnabled() && await this.authEmailService.hasPendingRegistration(email)) {
1680
+ if (this.isEmailVerificationEnabled() && await this.authEmailService.hasPendingRegistration(email, password)) {
1574
1681
  return {
1575
1682
  requiresEmailVerification: true,
1576
1683
  email,
@@ -1719,12 +1826,20 @@ class AuthenticationService {
1719
1826
  return await this.createUserDirectly(dto);
1720
1827
  }
1721
1828
  async handleExistingUserCompanyRegistration(existingUser, dto) {
1722
- if (!dto.companySlug && !dto.newCompanyName) {
1829
+ const ownsAccount = await external_bcrypt_.compare(dto.password, existingUser.password || TIMING_SAFE_DUMMY_HASH);
1830
+ if (!dto.companySlug && !dto.newCompanyName || !ownsAccount || !existingUser.password) {
1723
1831
  throw new common_.BadRequestException({
1724
1832
  message: 'Email already registered. Please login or provide a company to join/create.',
1725
1833
  messageKey: message_keys/* .AUTH_MESSAGES.REGISTER_EMAIL_EXISTS */.mK.REGISTER_EMAIL_EXISTS
1726
1834
  });
1727
1835
  }
1836
+ this.ensureUserActive(existingUser);
1837
+ if (this.isEmailVerificationEnabled() && !existingUser.emailVerified) {
1838
+ throw new common_.BadRequestException({
1839
+ message: 'Please verify your email address before joining a company.',
1840
+ messageKey: message_keys/* .AUTH_MESSAGES.LOGIN_EMAIL_NOT_VERIFIED */.mK.LOGIN_EMAIL_NOT_VERIFIED
1841
+ });
1842
+ }
1728
1843
  const permissionRepository = await this.getCompanyScopedRepository(user_company_permission_entity/* .UserCompanyPermission */.I);
1729
1844
  const companyRepository = await this.getCompanyScopedRepository(company_entity/* .Company */.C);
1730
1845
  if (!permissionRepository || !companyRepository) {
@@ -1788,6 +1903,7 @@ class AuthenticationService {
1788
1903
  await this.userEnricher.onUserCreated(user.id, dto.additionalFields ?? null, queryRunner);
1789
1904
  }
1790
1905
  await queryRunner.commitTransaction();
1906
+ await this.invalidateCaches(event_actions/* .AUTH_EVENT_ENTITIES.USER */.g.USER);
1791
1907
  if (!this.isCompanyFeatureEnabled()) {
1792
1908
  return this.generateRegistrationResponse(user);
1793
1909
  }
@@ -1849,6 +1965,7 @@ class AuthenticationService {
1849
1965
  await this.userEnricher.onUserCreated(user.id, pending.additionalFields ?? null, queryRunner);
1850
1966
  }
1851
1967
  await queryRunner.commitTransaction();
1968
+ await this.invalidateCaches(event_actions/* .AUTH_EVENT_ENTITIES.USER */.g.USER);
1852
1969
  if (this.isCompanyFeatureEnabled() && pending.companyData) {
1853
1970
  return await this.processCompanyRegistration(user, pending.companyData);
1854
1971
  }
@@ -1881,8 +1998,12 @@ class AuthenticationService {
1881
1998
  return this.generateRegistrationResponse(user);
1882
1999
  }
1883
2000
  const { company, branch } = await this.findOrCreateCompanyAndBranch(data, companyRepository, branchRepository);
2001
+ if (data.newCompanyName && company) {
2002
+ await this.invalidateCaches(event_actions/* .AUTH_EVENT_ENTITIES.COMPANY */.g.COMPANY, event_actions/* .AUTH_EVENT_ENTITIES.COMPANY_BRANCH */.g.COMPANY_BRANCH);
2003
+ }
1884
2004
  if (company) {
1885
2005
  await this.assignUserPermissions(user.id, company.id, branch?.id, permissionRepository);
2006
+ await this.invalidateCaches(event_actions/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION);
1886
2007
  return this.generateRegistrationResponseWithCompany(user, company, branch ?? undefined);
1887
2008
  }
1888
2009
  return this.generateRegistrationResponse(user);
@@ -1979,8 +2100,9 @@ class AuthenticationService {
1979
2100
  messageKey: message_keys/* .AUTH_MESSAGES.SESSION_NOT_AVAILABLE */.mK.SESSION_NOT_AVAILABLE
1980
2101
  });
1981
2102
  }
1982
- const session = await this.sessionService.consumeSession(sessionId);
1983
- if (!session) {
2103
+ const session = await this.sessionService.consumeSession(sessionId, (0,utils_.cacheTenantId)(this.dataSourceProvider));
2104
+ // A session started before a logout / password reset must not mint tokens after it
2105
+ if (!session || await this.isRevokedSince(session.userId, new Date(session.createdAt).getTime())) {
1984
2106
  throw new common_.UnauthorizedException({
1985
2107
  message: 'Invalid or expired session',
1986
2108
  messageKey: message_keys/* .AUTH_MESSAGES.SESSION_INVALID */.mK.SESSION_INVALID
@@ -2005,6 +2127,7 @@ class AuthenticationService {
2005
2127
  messageKey: message_keys/* .USER_MESSAGES.NOT_FOUND */.ny.NOT_FOUND
2006
2128
  });
2007
2129
  }
2130
+ this.ensureUserActive(user);
2008
2131
  const permissionRepository = await this.getCompanyScopedRepository(user_company_permission_entity/* .UserCompanyPermission */.I);
2009
2132
  const companyRepository = await this.getCompanyScopedRepository(company_entity/* .Company */.C);
2010
2133
  const branchRepository = await this.getCompanyScopedRepository(company_branch_entity/* .CompanyBranch */.A);
@@ -2084,7 +2207,13 @@ class AuthenticationService {
2084
2207
  'HS256'
2085
2208
  ]
2086
2209
  });
2087
- if (await this.isTokenRevoked(payload.id, payload.iat)) {
2210
+ if (payload.type !== 'refresh') {
2211
+ throw new common_.UnauthorizedException({
2212
+ message: 'Invalid refresh token',
2213
+ messageKey: message_keys/* .AUTH_MESSAGES.TOKEN_INVALID */.mK.TOKEN_INVALID
2214
+ });
2215
+ }
2216
+ if (await this.isTokenRevoked(payload.id, payload.iat, payload.iatMs)) {
2088
2217
  throw new common_.UnauthorizedException({
2089
2218
  message: 'Token has been revoked',
2090
2219
  messageKey: message_keys/* .AUTH_MESSAGES.TOKEN_REVOKED */.mK.TOKEN_REVOKED
@@ -2229,6 +2358,7 @@ class AuthenticationService {
2229
2358
  }
2230
2359
  user.password = await external_bcrypt_.hash(newPassword, (/* inlined export .BCRYPT_SALT_ROUNDS */12));
2231
2360
  await userRepository.save(user);
2361
+ await this.invalidateCaches(event_actions/* .AUTH_EVENT_ENTITIES.USER */.g.USER);
2232
2362
  this.publishAuthEvent(event_actions/* .AUTH_EVENT_ENTITIES.USER */.g.USER, event_actions/* .AUTH_EVENT_ACTIONS.PASSWORD_CHANGED */.o.PASSWORD_CHANGED, user);
2233
2363
  }
2234
2364
  async logout(userId) {
@@ -2251,16 +2381,14 @@ class AuthenticationService {
2251
2381
  }
2252
2382
  });
2253
2383
  }
2254
- async isTokenRevoked(userId, tokenIssuedAt) {
2255
- if (!this.cacheManager || !tokenIssuedAt) {
2256
- return false;
2257
- }
2258
- const cacheKey = `${auth_constants/* .TOKEN_REVOKED_PREFIX */.EV}:${userId}`;
2259
- const revokedAt = await this.cacheManager.get(cacheKey);
2260
- if (!revokedAt) {
2261
- return false;
2262
- }
2263
- return tokenIssuedAt < revokedAt;
2384
+ /** `tokenIssuedAtMs` is the token's `iatMs`; a token without it is judged by `iat` (seconds), erring towards revoked */ async isTokenRevoked(userId, tokenIssuedAt, tokenIssuedAtMs) {
2385
+ const issuedAtMs = typeof tokenIssuedAtMs === 'number' ? tokenIssuedAtMs : tokenIssuedAt ? tokenIssuedAt * 1000 : undefined;
2386
+ if (issuedAtMs === undefined) return false;
2387
+ return this.isRevokedSince(userId, issuedAtMs);
2388
+ }
2389
+ async isRevokedSince(userId, issuedAtMs) {
2390
+ if (!this.cacheManager) return false;
2391
+ return (0,helpers/* .isIssuedBeforeRevocation */.lL)(this.cacheManager, userId, issuedAtMs);
2264
2392
  }
2265
2393
  async getUserCompanies(userId) {
2266
2394
  this.ensureCompanyFeatureEnabled();
@@ -2415,7 +2543,8 @@ class AuthenticationService {
2415
2543
  email
2416
2544
  }
2417
2545
  });
2418
- if (!user || !await external_bcrypt_.compare(password, user.password || '')) {
2546
+ const passwordMatches = await external_bcrypt_.compare(password, user?.password || TIMING_SAFE_DUMMY_HASH);
2547
+ if (!user?.password || !passwordMatches) {
2419
2548
  throw new common_.UnauthorizedException({
2420
2549
  message: 'Invalid credentials',
2421
2550
  messageKey: message_keys/* .AUTH_MESSAGES.LOGIN_INVALID_CREDENTIALS */.mK.LOGIN_INVALID_CREDENTIALS
@@ -2423,6 +2552,7 @@ class AuthenticationService {
2423
2552
  }
2424
2553
  user.lastLoginAt = new Date();
2425
2554
  await userRepository.save(user);
2555
+ await this.invalidateCaches(event_actions/* .AUTH_EVENT_ENTITIES.USER */.g.USER);
2426
2556
  return user;
2427
2557
  }
2428
2558
  generateLoginResponse(user) {
@@ -2480,7 +2610,7 @@ class AuthenticationService {
2480
2610
  messageKey: message_keys/* .AUTH_MESSAGES.SESSION_NOT_AVAILABLE */.mK.SESSION_NOT_AVAILABLE
2481
2611
  });
2482
2612
  }
2483
- const sessionId = await this.sessionService.createSession(user.id);
2613
+ const sessionId = await this.sessionService.createSession(user.id, (0,utils_.cacheTenantId)(this.dataSourceProvider));
2484
2614
  const companiesWithBranches = companies.map((company)=>({
2485
2615
  ...(0,helpers/* .toCompanyInfo */.$C)(company),
2486
2616
  isActive: company.isActive,
@@ -2843,6 +2973,12 @@ class BranchService extends _flusys_nestjs_shared_classes__rspack_import_0.ApiSe
2843
2973
  });
2844
2974
  await Promise.all(rows.map((row)=>this.permissionSyncAdapter.onBranchDeleted(row.id, row.companyId)));
2845
2975
  }
2976
+ // beforeDeleteOperation drops the branches' grants; bump after commit
2977
+ async delete(option, user) {
2978
+ const result = await super.delete(option, user);
2979
+ await this.utilsService.clearCache(_config_index_js__rspack_import_12/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION, this.cacheManager, this.cacheTenant());
2980
+ return result;
2981
+ }
2846
2982
  // Hierarchy - ids that are empty or not UUIDs match no branch (and never reach a uuid column)
2847
2983
  async getDescendantIds(branchIds, options = {}) {
2848
2984
  const ids = (0,_flusys_nestjs_shared_utils__rspack_import_3.normalizeBranchIds)(branchIds).filter((id)=>(0,class_validator__rspack_import_5.isUUID)(id));
@@ -2978,47 +3114,39 @@ class CompanySelectionSessionService {
2978
3114
  constructor(cacheManager){
2979
3115
  this.cacheManager = cacheManager;
2980
3116
  }
2981
- async createSession(userId) {
3117
+ async createSession(userId, tenantId) {
2982
3118
  const sessionId = (0,crypto__rspack_import_3.randomUUID)();
2983
- const now = new Date();
2984
- const expiresAt = new Date(now.getTime() + SESSION_TTL_MS);
3119
+ const now = Date.now();
2985
3120
  const session = {
2986
3121
  sessionId,
2987
3122
  userId,
2988
3123
  createdAt: now,
2989
- expiresAt
3124
+ expiresAt: now + SESSION_TTL_MS
2990
3125
  };
2991
- await this.cacheManager.set(this.getKey(sessionId), session, SESSION_TTL_MS);
3126
+ await this.cacheManager.set(this.getKey(sessionId, tenantId), session, SESSION_TTL_MS, {
3127
+ skipMemory: true
3128
+ });
2992
3129
  return sessionId;
2993
3130
  }
2994
- /**
2995
- * Get session by ID (does not consume/delete)
2996
- */ async getSession(sessionId) {
2997
- const session = await this.cacheManager.get(this.getKey(sessionId));
2998
- if (!session) return null;
2999
- // Check if expired (extra safety, cache should handle TTL)
3000
- if (new Date() > new Date(session.expiresAt)) {
3001
- await this.deleteSession(sessionId);
3002
- return null;
3003
- }
3004
- return session;
3131
+ /** Reads a session without consuming it */ async getSession(sessionId, tenantId) {
3132
+ const session = await this.cacheManager.get(this.getKey(sessionId, tenantId), {
3133
+ skipMemory: true
3134
+ });
3135
+ return this.unexpired(session);
3005
3136
  }
3006
- async consumeSession(sessionId) {
3007
- const session = await this.getSession(sessionId);
3008
- if (session) {
3009
- await this.deleteSession(sessionId);
3010
- }
3011
- return session;
3137
+ /** Atomic across instances: of two concurrent calls for one session, only one receives it */ async consumeSession(sessionId, tenantId) {
3138
+ const session = await this.cacheManager.take(this.getKey(sessionId, tenantId), SESSION_TTL_MS);
3139
+ return this.unexpired(session);
3012
3140
  }
3013
- /**
3014
- * Delete session
3015
- */ async deleteSession(sessionId) {
3016
- await this.cacheManager.del(this.getKey(sessionId));
3141
+ async deleteSession(sessionId, tenantId) {
3142
+ await this.cacheManager.del(this.getKey(sessionId, tenantId));
3017
3143
  }
3018
- /**
3019
- * Get cache key for session
3020
- */ getKey(sessionId) {
3021
- return `${_config_auth_constants_js__rspack_import_4/* .SESSION_CACHE_PREFIX */.Vl}:${sessionId}`;
3144
+ unexpired(session) {
3145
+ if (!session || !(new Date(session.expiresAt).getTime() >= Date.now())) return null;
3146
+ return session;
3147
+ }
3148
+ getKey(sessionId, tenantId) {
3149
+ return tenantId ? `${_config_auth_constants_js__rspack_import_4/* .SESSION_CACHE_PREFIX */.Vl}:${tenantId}:${sessionId}` : `${_config_auth_constants_js__rspack_import_4/* .SESSION_CACHE_PREFIX */.Vl}:${sessionId}`;
3022
3150
  }
3023
3151
  }
3024
3152
  _ts_decorate([
@@ -3028,6 +3156,7 @@ _ts_decorate([
3028
3156
  }),
3029
3157
  _ts_metadata("design:type", Function),
3030
3158
  _ts_metadata("design:paramtypes", [
3159
+ String,
3031
3160
  String
3032
3161
  ]),
3033
3162
  _ts_metadata("design:returntype", Promise)
@@ -3039,6 +3168,7 @@ _ts_decorate([
3039
3168
  }),
3040
3169
  _ts_metadata("design:type", Function),
3041
3170
  _ts_metadata("design:paramtypes", [
3171
+ String,
3042
3172
  String
3043
3173
  ]),
3044
3174
  _ts_metadata("design:returntype", Promise)
@@ -3152,6 +3282,15 @@ class CompanyService extends _flusys_nestjs_shared_classes__rspack_import_0.ApiS
3152
3282
  }
3153
3283
  await Promise.all(companyIds.map((companyId)=>this.permissionSyncAdapter.onCompanyDeleted(companyId)));
3154
3284
  }
3285
+ // beforeDeleteOperation drops the company's grants (and a purge cascades its branches); bump after commit
3286
+ async delete(option, user) {
3287
+ const result = await super.delete(option, user);
3288
+ await Promise.all([
3289
+ _config_index_js__rspack_import_9/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION,
3290
+ _config_index_js__rspack_import_9/* .AUTH_EVENT_ENTITIES.COMPANY_BRANCH */.g.COMPANY_BRANCH
3291
+ ].map((name)=>this.utilsService.clearCache(name, this.cacheManager, this.cacheTenant())));
3292
+ return result;
3293
+ }
3155
3294
  // Query Customization
3156
3295
  async getSelectQuery(query, _user, select) {
3157
3296
  if (!select?.length) {
@@ -3286,6 +3425,14 @@ class SocialAuthConfigService extends _flusys_nestjs_shared_classes__rspack_impo
3286
3425
  });
3287
3426
  }
3288
3427
  }
3428
+ // The secret never leaves the service: not to clients, not into the shared cache
3429
+ convertEntityToResponseDto(entity, _isRaw) {
3430
+ const { clientSecret, ...rest } = entity;
3431
+ return {
3432
+ ...rest,
3433
+ hasClientSecret: !!clientSecret
3434
+ };
3435
+ }
3289
3436
  async getExtraManipulateQuery(query, filterDto, user) {
3290
3437
  const result = await super.getExtraManipulateQuery(query, filterDto, user);
3291
3438
  query.orderBy(`${this.entityName}.provider`, 'ASC');
@@ -3320,15 +3467,18 @@ __webpack_require__.d(__webpack_exports__, {
3320
3467
  /* import */ var _nestjs_common__rspack_import_3 = __webpack_require__(6157);
3321
3468
  /* import */ var crypto__rspack_import_4 = __webpack_require__(7823);
3322
3469
  /* import */ var typeorm__rspack_import_5 = __webpack_require__(7158);
3323
- /* import */ var _config_index_js__rspack_import_12 = __webpack_require__(5363);
3324
- /* import */ var _config_index_js__rspack_import_13 = __webpack_require__(7987);
3325
- /* import */ var _config_index_js__rspack_import_11 = __webpack_require__(5588);
3470
+ /* import */ var _config_index_js__rspack_import_14 = __webpack_require__(5363);
3471
+ /* import */ var _config_index_js__rspack_import_15 = __webpack_require__(7987);
3472
+ /* import */ var _config_index_js__rspack_import_13 = __webpack_require__(5588);
3326
3473
  /* import */ var _entities_app_user_entity_js__rspack_import_6 = __webpack_require__(4758);
3327
3474
  /* import */ var _entities_social_auth_config_entity_js__rspack_import_7 = __webpack_require__(2020);
3328
- /* import */ var _enums_social_provider_type_enum_js__rspack_import_14 = __webpack_require__(5142);
3329
- /* import */ var _providers_social_provider_registry_js__rspack_import_8 = __webpack_require__(6928);
3330
- /* import */ var _auth_datasource_provider_js__rspack_import_9 = __webpack_require__(2732);
3331
- /* import */ var _authentication_service_js__rspack_import_10 = __webpack_require__(4722);
3475
+ /* import */ var _enums_social_provider_type_enum_js__rspack_import_17 = __webpack_require__(5142);
3476
+ /* import */ var _helpers_cache_invalidation_helper_js__rspack_import_8 = __webpack_require__(37);
3477
+ /* import */ var _helpers_token_helper_js__rspack_import_16 = __webpack_require__(1379);
3478
+ /* import */ var _providers_social_provider_registry_js__rspack_import_9 = __webpack_require__(6928);
3479
+ /* import */ var _auth_config_service_js__rspack_import_10 = __webpack_require__(6514);
3480
+ /* import */ var _auth_datasource_provider_js__rspack_import_11 = __webpack_require__(2732);
3481
+ /* import */ var _authentication_service_js__rspack_import_12 = __webpack_require__(4722);
3332
3482
  function _ts_decorate(decorators, target, key, desc) {
3333
3483
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3334
3484
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -3365,14 +3515,19 @@ function _ts_param(paramIndex, decorator) {
3365
3515
 
3366
3516
 
3367
3517
 
3518
+
3519
+
3520
+
3368
3521
  class SocialAuthService {
3369
3522
  authService;
3370
3523
  dataSourceProvider;
3371
3524
  cacheManager;
3372
- constructor(authService, dataSourceProvider, cacheManager){
3525
+ authConfig;
3526
+ constructor(authService, dataSourceProvider, cacheManager, authConfig){
3373
3527
  this.authService = authService;
3374
3528
  this.dataSourceProvider = dataSourceProvider;
3375
3529
  this.cacheManager = cacheManager;
3530
+ this.authConfig = authConfig;
3376
3531
  }
3377
3532
  async listEnabledProviders(redirectUri) {
3378
3533
  const repository = await this.dataSourceProvider.getRepository(_entities_social_auth_config_entity_js__rspack_import_7/* .SocialAuthConfig */.G);
@@ -3393,21 +3548,23 @@ class SocialAuthService {
3393
3548
  await this.getCache().set(this.stateKey(state), {
3394
3549
  provider,
3395
3550
  redirectUri
3396
- }, (/* inlined export .SOCIAL_STATE_TTL_MS */600000));
3551
+ }, (/* inlined export .SOCIAL_STATE_TTL_MS */600000), {
3552
+ skipMemory: true
3553
+ });
3397
3554
  return {
3398
3555
  provider,
3399
- authorizationUrl: (0,_providers_social_provider_registry_js__rspack_import_8/* .getSocialProvider */.V)(provider).buildAuthorizationUrl(this.toCredentials(config, redirectUri), state),
3556
+ authorizationUrl: (0,_providers_social_provider_registry_js__rspack_import_9/* .getSocialProvider */.V)(provider).buildAuthorizationUrl(this.toCredentials(config, redirectUri), state),
3400
3557
  state
3401
3558
  };
3402
3559
  }
3403
3560
  async completeLogin(code, state, redirectUri) {
3404
3561
  const provider = await this.consumeState(state, redirectUri);
3405
3562
  const config = await this.getUsableConfig(provider);
3406
- const profile = await (0,_providers_social_provider_registry_js__rspack_import_8/* .getSocialProvider */.V)(provider).fetchProfile(this.toCredentials(config, redirectUri), code);
3563
+ const profile = await (0,_providers_social_provider_registry_js__rspack_import_9/* .getSocialProvider */.V)(provider).fetchProfile(this.toCredentials(config, redirectUri), code);
3407
3564
  if (!profile.providerUserId) {
3408
3565
  throw new _nestjs_common__rspack_import_3.UnauthorizedException({
3409
3566
  message: `${provider} did not return an account identifier`,
3410
- messageKey: _config_index_js__rspack_import_12/* .SOCIAL_AUTH_MESSAGES.PROFILE_INCOMPLETE */.OF.PROFILE_INCOMPLETE,
3567
+ messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.PROFILE_INCOMPLETE */.OF.PROFILE_INCOMPLETE,
3411
3568
  messageVariables: {
3412
3569
  provider
3413
3570
  }
@@ -3415,7 +3572,7 @@ class SocialAuthService {
3415
3572
  }
3416
3573
  const user = await this.resolveUser(provider, profile);
3417
3574
  const response = await this.authService.issueLoginForUser(user);
3418
- this.publishSocialEvent(_config_index_js__rspack_import_13/* .AUTH_EVENT_ENTITIES.SESSION */.g.SESSION, _config_index_js__rspack_import_13/* .AUTH_EVENT_ACTIONS.SOCIAL_LOGGED_IN */.o.SOCIAL_LOGGED_IN, user, provider);
3575
+ this.publishSocialEvent(_config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.SESSION */.g.SESSION, _config_index_js__rspack_import_15/* .AUTH_EVENT_ACTIONS.SOCIAL_LOGGED_IN */.o.SOCIAL_LOGGED_IN, user, provider);
3419
3576
  return response;
3420
3577
  }
3421
3578
  async resolveUser(provider, profile) {
@@ -3430,27 +3587,33 @@ class SocialAuthService {
3430
3587
  if (existing?.deletedAt) {
3431
3588
  throw new _nestjs_common__rspack_import_3.UnauthorizedException({
3432
3589
  message: 'This account has been permanently deleted. Please contact your system administrator.',
3433
- messageKey: _config_index_js__rspack_import_12/* .SOCIAL_AUTH_MESSAGES.ACCOUNT_DELETED */.OF.ACCOUNT_DELETED
3590
+ messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.ACCOUNT_DELETED */.OF.ACCOUNT_DELETED
3434
3591
  });
3435
3592
  }
3436
3593
  if (existing) {
3437
- // The provider already proved ownership of this email (ensureVerifiedEmail above),
3438
- // so a social login carries at least as much proof as clicking a verification link.
3439
- if (!existing.emailVerified) {
3440
- existing.emailVerified = true;
3441
- await userRepository.save(existing);
3442
- }
3594
+ if (!existing.emailVerified) await this.claimUnverifiedAccount(userRepository, existing);
3443
3595
  return existing;
3444
3596
  }
3445
3597
  const user = await this.registerUser(provider, userRepository, profile);
3446
- this.publishSocialEvent(_config_index_js__rspack_import_13/* .AUTH_EVENT_ENTITIES.USER */.g.USER, _config_index_js__rspack_import_13/* .AUTH_EVENT_ACTIONS.REGISTERED */.o.REGISTERED, user, provider);
3598
+ this.publishSocialEvent(_config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.USER */.g.USER, _config_index_js__rspack_import_15/* .AUTH_EVENT_ACTIONS.REGISTERED */.o.REGISTERED, user, provider);
3447
3599
  return user;
3448
3600
  }
3601
+ async claimUnverifiedAccount(userRepository, user) {
3602
+ user.emailVerified = true;
3603
+ user.password = null;
3604
+ await userRepository.save(user);
3605
+ await (0,_helpers_cache_invalidation_helper_js__rspack_import_8/* .invalidateEntityCaches */.i)(this.cacheManager, this.dataSourceProvider, [
3606
+ _config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.USER */.g.USER
3607
+ ]);
3608
+ if (this.cacheManager && this.authConfig) {
3609
+ await (0,_helpers_token_helper_js__rspack_import_16/* .revokeUserSessions */.XP)(this.cacheManager, this.authConfig, user.id);
3610
+ }
3611
+ }
3449
3612
  ensureVerifiedEmail(provider, profile) {
3450
3613
  if (!profile.email) {
3451
3614
  throw new _nestjs_common__rspack_import_3.UnauthorizedException({
3452
3615
  message: `${provider} did not share an email address`,
3453
- messageKey: _config_index_js__rspack_import_12/* .SOCIAL_AUTH_MESSAGES.EMAIL_REQUIRED */.OF.EMAIL_REQUIRED,
3616
+ messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.EMAIL_REQUIRED */.OF.EMAIL_REQUIRED,
3454
3617
  messageVariables: {
3455
3618
  provider
3456
3619
  }
@@ -3459,7 +3622,7 @@ class SocialAuthService {
3459
3622
  if (!profile.emailVerified) {
3460
3623
  throw new _nestjs_common__rspack_import_3.UnauthorizedException({
3461
3624
  message: `${provider} has not verified this email address`,
3462
- messageKey: _config_index_js__rspack_import_12/* .SOCIAL_AUTH_MESSAGES.EMAIL_NOT_VERIFIED */.OF.EMAIL_NOT_VERIFIED,
3625
+ messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.EMAIL_NOT_VERIFIED */.OF.EMAIL_NOT_VERIFIED,
3463
3626
  messageVariables: {
3464
3627
  provider
3465
3628
  }
@@ -3470,7 +3633,7 @@ class SocialAuthService {
3470
3633
  if (!this.dataSourceProvider.isSignUpEnabled()) {
3471
3634
  throw new _nestjs_common__rspack_import_3.UnauthorizedException({
3472
3635
  message: `No account is linked to this ${provider} profile`,
3473
- messageKey: _config_index_js__rspack_import_12/* .SOCIAL_AUTH_MESSAGES.ACCOUNT_NOT_LINKED */.OF.ACCOUNT_NOT_LINKED,
3636
+ messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.ACCOUNT_NOT_LINKED */.OF.ACCOUNT_NOT_LINKED,
3474
3637
  messageVariables: {
3475
3638
  provider
3476
3639
  }
@@ -3479,19 +3642,23 @@ class SocialAuthService {
3479
3642
  if (this.dataSourceProvider.isCompanyFeatureEnabled()) {
3480
3643
  throw new _nestjs_common__rspack_import_3.UnauthorizedException({
3481
3644
  message: `No account is linked to this ${provider} profile. Please sign up manually and join or create a company first, then social sign-in will work.`,
3482
- messageKey: _config_index_js__rspack_import_12/* .SOCIAL_AUTH_MESSAGES.COMPANY_REQUIRED */.OF.COMPANY_REQUIRED,
3645
+ messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.COMPANY_REQUIRED */.OF.COMPANY_REQUIRED,
3483
3646
  messageVariables: {
3484
3647
  provider
3485
3648
  }
3486
3649
  });
3487
3650
  }
3488
- return userRepository.save(userRepository.create({
3651
+ const user = await userRepository.save(userRepository.create({
3489
3652
  email: profile.email,
3490
3653
  name: profile.name,
3491
3654
  password: null,
3492
3655
  isActive: true,
3493
3656
  emailVerified: true
3494
3657
  }));
3658
+ await (0,_helpers_cache_invalidation_helper_js__rspack_import_8/* .invalidateEntityCaches */.i)(this.cacheManager, this.dataSourceProvider, [
3659
+ _config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.USER */.g.USER
3660
+ ]);
3661
+ return user;
3495
3662
  }
3496
3663
  async getUsableConfig(provider) {
3497
3664
  const repository = await this.dataSourceProvider.getRepository(_entities_social_auth_config_entity_js__rspack_import_7/* .SocialAuthConfig */.G);
@@ -3504,7 +3671,7 @@ class SocialAuthService {
3504
3671
  if (!config?.isEnabled) {
3505
3672
  throw new _nestjs_common__rspack_import_3.BadRequestException({
3506
3673
  message: `Social sign-in with ${provider} is not enabled`,
3507
- messageKey: _config_index_js__rspack_import_12/* .SOCIAL_AUTH_MESSAGES.PROVIDER_DISABLED */.OF.PROVIDER_DISABLED,
3674
+ messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.PROVIDER_DISABLED */.OF.PROVIDER_DISABLED,
3508
3675
  messageVariables: {
3509
3676
  provider
3510
3677
  }
@@ -3513,7 +3680,7 @@ class SocialAuthService {
3513
3680
  if (!config.clientId || !config.clientSecret) {
3514
3681
  throw new _nestjs_common__rspack_import_3.BadRequestException({
3515
3682
  message: `Social sign-in with ${provider} is missing its client credentials`,
3516
- messageKey: _config_index_js__rspack_import_12/* .SOCIAL_AUTH_MESSAGES.PROVIDER_NOT_CONFIGURED */.OF.PROVIDER_NOT_CONFIGURED,
3683
+ messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.PROVIDER_NOT_CONFIGURED */.OF.PROVIDER_NOT_CONFIGURED,
3517
3684
  messageVariables: {
3518
3685
  provider
3519
3686
  }
@@ -3531,25 +3698,24 @@ class SocialAuthService {
3531
3698
  };
3532
3699
  }
3533
3700
  async consumeState(state, redirectUri) {
3534
- const key = this.stateKey(state);
3535
- const stored = await this.getCache().get(key);
3536
- await this.getCache().del(key);
3701
+ // Atomic across instances: a state (and the code that comes with it) is replayable nowhere
3702
+ const stored = await this.getCache().take(this.stateKey(state), (/* inlined export .SOCIAL_STATE_TTL_MS */600000));
3537
3703
  if (!stored) {
3538
3704
  throw new _nestjs_common__rspack_import_3.UnauthorizedException({
3539
3705
  message: 'Social sign-in session is invalid or has expired',
3540
- messageKey: _config_index_js__rspack_import_12/* .SOCIAL_AUTH_MESSAGES.STATE_INVALID */.OF.STATE_INVALID
3706
+ messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.STATE_INVALID */.OF.STATE_INVALID
3541
3707
  });
3542
3708
  }
3543
3709
  if (stored.redirectUri !== redirectUri) {
3544
3710
  throw new _nestjs_common__rspack_import_3.UnauthorizedException({
3545
3711
  message: 'Redirect URI does not match the one the sign-in started with',
3546
- messageKey: _config_index_js__rspack_import_12/* .SOCIAL_AUTH_MESSAGES.REDIRECT_URI_MISMATCH */.OF.REDIRECT_URI_MISMATCH
3712
+ messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.REDIRECT_URI_MISMATCH */.OF.REDIRECT_URI_MISMATCH
3547
3713
  });
3548
3714
  }
3549
3715
  return stored.provider;
3550
3716
  }
3551
3717
  stateKey(state) {
3552
- return `${_config_index_js__rspack_import_11/* .SOCIAL_STATE_PREFIX */.PR}:${state}`;
3718
+ return `${_config_index_js__rspack_import_13/* .SOCIAL_STATE_PREFIX */.PR}:${state}`;
3553
3719
  }
3554
3720
  getCache() {
3555
3721
  if (!this.cacheManager) {
@@ -3591,7 +3757,7 @@ _ts_decorate([
3591
3757
  }),
3592
3758
  _ts_metadata("design:type", Function),
3593
3759
  _ts_metadata("design:paramtypes", [
3594
- typeof _enums_social_provider_type_enum_js__rspack_import_14/* .SocialProviderTypeEnum */.u === "undefined" ? Object : _enums_social_provider_type_enum_js__rspack_import_14/* .SocialProviderTypeEnum */.u,
3760
+ typeof _enums_social_provider_type_enum_js__rspack_import_17/* .SocialProviderTypeEnum */.u === "undefined" ? Object : _enums_social_provider_type_enum_js__rspack_import_17/* .SocialProviderTypeEnum */.u,
3595
3761
  String
3596
3762
  ]),
3597
3763
  _ts_metadata("design:returntype", Promise)
@@ -3614,15 +3780,18 @@ SocialAuthService = _ts_decorate([
3614
3780
  (0,_nestjs_common__rspack_import_3.Injectable)({
3615
3781
  scope: _nestjs_common__rspack_import_3.Scope.REQUEST
3616
3782
  }),
3617
- _ts_param(0, (0,_nestjs_common__rspack_import_3.Inject)(_authentication_service_js__rspack_import_10/* .AuthenticationService */.k)),
3618
- _ts_param(1, (0,_nestjs_common__rspack_import_3.Inject)(_auth_datasource_provider_js__rspack_import_9/* .AuthDataSourceProvider */.v)),
3783
+ _ts_param(0, (0,_nestjs_common__rspack_import_3.Inject)(_authentication_service_js__rspack_import_12/* .AuthenticationService */.k)),
3784
+ _ts_param(1, (0,_nestjs_common__rspack_import_3.Inject)(_auth_datasource_provider_js__rspack_import_11/* .AuthDataSourceProvider */.v)),
3619
3785
  _ts_param(2, (0,_nestjs_common__rspack_import_3.Optional)()),
3620
3786
  _ts_param(2, (0,_nestjs_common__rspack_import_3.Inject)('CACHE_INSTANCE')),
3787
+ _ts_param(3, (0,_nestjs_common__rspack_import_3.Optional)()),
3788
+ _ts_param(3, (0,_nestjs_common__rspack_import_3.Inject)(_auth_config_service_js__rspack_import_10/* .AuthConfigService */.W)),
3621
3789
  _ts_metadata("design:type", Function),
3622
3790
  _ts_metadata("design:paramtypes", [
3623
- typeof _authentication_service_js__rspack_import_10/* .AuthenticationService */.k === "undefined" ? Object : _authentication_service_js__rspack_import_10/* .AuthenticationService */.k,
3624
- typeof _auth_datasource_provider_js__rspack_import_9/* .AuthDataSourceProvider */.v === "undefined" ? Object : _auth_datasource_provider_js__rspack_import_9/* .AuthDataSourceProvider */.v,
3625
- typeof _flusys_nestjs_shared_classes__rspack_import_1.HybridCache === "undefined" ? Object : _flusys_nestjs_shared_classes__rspack_import_1.HybridCache
3791
+ typeof _authentication_service_js__rspack_import_12/* .AuthenticationService */.k === "undefined" ? Object : _authentication_service_js__rspack_import_12/* .AuthenticationService */.k,
3792
+ typeof _auth_datasource_provider_js__rspack_import_11/* .AuthDataSourceProvider */.v === "undefined" ? Object : _auth_datasource_provider_js__rspack_import_11/* .AuthDataSourceProvider */.v,
3793
+ typeof _flusys_nestjs_shared_classes__rspack_import_1.HybridCache === "undefined" ? Object : _flusys_nestjs_shared_classes__rspack_import_1.HybridCache,
3794
+ typeof _auth_config_service_js__rspack_import_10/* .AuthConfigService */.W === "undefined" ? Object : _auth_config_service_js__rspack_import_10/* .AuthConfigService */.W
3626
3795
  ])
3627
3796
  ], SocialAuthService);
3628
3797
 
@@ -3637,12 +3806,12 @@ __webpack_require__.d(__webpack_exports__, {
3637
3806
  /* import */ var _flusys_nestjs_shared_classes__rspack_import_1 = __webpack_require__(6937);
3638
3807
  /* import */ var _flusys_nestjs_shared_interfaces__rspack_import_2 = __webpack_require__(5103);
3639
3808
  /* import */ var _nestjs_common__rspack_import_3 = __webpack_require__(6157);
3640
- /* import */ var _config_index_js__rspack_import_9 = __webpack_require__(5363);
3641
- /* import */ var _config_index_js__rspack_import_10 = __webpack_require__(7987);
3809
+ /* import */ var _config_index_js__rspack_import_9 = __webpack_require__(7987);
3810
+ /* import */ var _config_index_js__rspack_import_10 = __webpack_require__(5363);
3642
3811
  /* import */ var _entities_company_branch_entity_js__rspack_import_4 = __webpack_require__(1923);
3643
3812
  /* import */ var _entities_user_company_permission_entity_js__rspack_import_5 = __webpack_require__(2377);
3644
3813
  /* import */ var _helpers_branch_ancestry_helper_js__rspack_import_6 = __webpack_require__(7526);
3645
- /* import */ var _auth_config_service_js__rspack_import_7 = __webpack_require__(6514);
3814
+ /* import */ var _helpers_cache_invalidation_helper_js__rspack_import_7 = __webpack_require__(37);
3646
3815
  /* import */ var _auth_datasource_provider_js__rspack_import_8 = __webpack_require__(2732);
3647
3816
  function _ts_decorate(decorators, target, key, desc) {
3648
3817
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -3677,20 +3846,28 @@ function _ts_param(paramIndex, decorator) {
3677
3846
 
3678
3847
 
3679
3848
 
3849
+ const UNIQUE_VIOLATION_CODES = new Set([
3850
+ 'ER_DUP_ENTRY',
3851
+ '23505',
3852
+ 'SQLITE_CONSTRAINT'
3853
+ ]);
3680
3854
  class UserPermissionService {
3681
3855
  dataSourceProvider;
3682
- authConfig;
3683
3856
  permissionSyncAdapter;
3857
+ cacheManager;
3684
3858
  _repository = null;
3685
- constructor(dataSourceProvider, authConfig, permissionSyncAdapter){
3859
+ constructor(dataSourceProvider, permissionSyncAdapter, cacheManager){
3686
3860
  this.dataSourceProvider = dataSourceProvider;
3687
- this.authConfig = authConfig;
3688
3861
  this.permissionSyncAdapter = permissionSyncAdapter;
3862
+ this.cacheManager = cacheManager;
3689
3863
  }
3690
- /**
3691
- * Ensures the acting user is only granting/revoking access within their own tenant.
3692
- * for a company or branch outside their own company.
3693
- */ async getRepository() {
3864
+ // The user list caches each user's grant; it depends on this stamp
3865
+ async invalidateGrantCache() {
3866
+ await (0,_helpers_cache_invalidation_helper_js__rspack_import_7/* .invalidateEntityCaches */.i)(this.cacheManager, this.dataSourceProvider, [
3867
+ _config_index_js__rspack_import_9/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION
3868
+ ]);
3869
+ }
3870
+ async getRepository() {
3694
3871
  if (!this._repository) {
3695
3872
  this._repository = await this.dataSourceProvider.getRepository(_entities_user_company_permission_entity_js__rspack_import_5/* .UserCompanyPermission */.I);
3696
3873
  }
@@ -3725,7 +3902,7 @@ class UserPermissionService {
3725
3902
  if (existing) {
3726
3903
  throw new _nestjs_common__rspack_import_3.ConflictException({
3727
3904
  message: `User already assigned to this ${typeName}`,
3728
- messageKey: _config_index_js__rspack_import_9/* .USER_PERMISSION_MESSAGES.ALREADY_ASSIGNED */.jz.ALREADY_ASSIGNED,
3905
+ messageKey: _config_index_js__rspack_import_10/* .USER_PERMISSION_MESSAGES.ALREADY_ASSIGNED */.jz.ALREADY_ASSIGNED,
3729
3906
  messageVariables: {
3730
3907
  type: typeName
3731
3908
  }
@@ -3739,10 +3916,10 @@ class UserPermissionService {
3739
3916
  }));
3740
3917
  } catch (error) {
3741
3918
  const err = error;
3742
- if (err?.code === 'ER_DUP_ENTRY' || String(err?.message).includes('Duplicate entry')) {
3919
+ if (UNIQUE_VIOLATION_CODES.has(String(err?.code)) || String(err?.message).includes('Duplicate entry')) {
3743
3920
  throw new _nestjs_common__rspack_import_3.ConflictException({
3744
3921
  message: `User already assigned to this ${typeName}. Please refresh and try again.`,
3745
- messageKey: _config_index_js__rspack_import_9/* .USER_PERMISSION_MESSAGES.ALREADY_ASSIGNED */.jz.ALREADY_ASSIGNED,
3922
+ messageKey: _config_index_js__rspack_import_10/* .USER_PERMISSION_MESSAGES.ALREADY_ASSIGNED */.jz.ALREADY_ASSIGNED,
3746
3923
  messageVariables: {
3747
3924
  type: typeName
3748
3925
  }
@@ -3750,7 +3927,8 @@ class UserPermissionService {
3750
3927
  }
3751
3928
  throw error;
3752
3929
  }
3753
- await this.publishPermissionEvent(_config_index_js__rspack_import_10/* .AUTH_EVENT_ACTIONS.PERMISSION_GRANTED */.o.PERMISSION_GRANTED, userId, targetId, type);
3930
+ await this.invalidateGrantCache();
3931
+ await this.publishPermissionEvent(_config_index_js__rspack_import_9/* .AUTH_EVENT_ACTIONS.PERMISSION_GRANTED */.o.PERMISSION_GRANTED, userId, targetId, type);
3754
3932
  }
3755
3933
  async revokePermission(userId, targetId, type) {
3756
3934
  const repository = await this.getRepository();
@@ -3768,7 +3946,7 @@ class UserPermissionService {
3768
3946
  if (!permission) {
3769
3947
  throw new _nestjs_common__rspack_import_3.NotFoundException({
3770
3948
  message: `${typeName.charAt(0).toUpperCase() + typeName.slice(1)} permission not found`,
3771
- messageKey: _config_index_js__rspack_import_9/* .USER_PERMISSION_MESSAGES.PERMISSION_NOT_FOUND */.jz.PERMISSION_NOT_FOUND,
3949
+ messageKey: _config_index_js__rspack_import_10/* .USER_PERMISSION_MESSAGES.PERMISSION_NOT_FOUND */.jz.PERMISSION_NOT_FOUND,
3772
3950
  messageVariables: {
3773
3951
  type: typeName.charAt(0).toUpperCase() + typeName.slice(1)
3774
3952
  }
@@ -3783,12 +3961,13 @@ class UserPermissionService {
3783
3961
  } else {
3784
3962
  if (companyId) await this.permissionSyncAdapter?.onUserBranchAccessRevoked?.(userId, targetId, companyId);
3785
3963
  }
3786
- await this.publishPermissionEvent(_config_index_js__rspack_import_10/* .AUTH_EVENT_ACTIONS.PERMISSION_REVOKED */.o.PERMISSION_REVOKED, userId, targetId, type, companyId);
3964
+ await this.invalidateGrantCache();
3965
+ await this.publishPermissionEvent(_config_index_js__rspack_import_9/* .AUTH_EVENT_ACTIONS.PERMISSION_REVOKED */.o.PERMISSION_REVOKED, userId, targetId, type, companyId);
3787
3966
  }
3788
3967
  async publishPermissionEvent(action, userId, targetId, type, companyId) {
3789
3968
  await (0,_flusys_nestjs_shared_classes__rspack_import_1.publishDomainEvent)({
3790
3969
  module: (/* inlined export .AUTH_EVENT_MODULE */"auth"),
3791
- entity: _config_index_js__rspack_import_10/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION,
3970
+ entity: _config_index_js__rspack_import_9/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION,
3792
3971
  action,
3793
3972
  ids: [
3794
3973
  targetId
@@ -3898,14 +4077,15 @@ UserPermissionService = _ts_decorate([
3898
4077
  scope: _nestjs_common__rspack_import_3.Scope.REQUEST
3899
4078
  }),
3900
4079
  _ts_param(0, (0,_nestjs_common__rspack_import_3.Inject)(_auth_datasource_provider_js__rspack_import_8/* .AuthDataSourceProvider */.v)),
3901
- _ts_param(1, (0,_nestjs_common__rspack_import_3.Inject)(_auth_config_service_js__rspack_import_7/* .AuthConfigService */.W)),
4080
+ _ts_param(1, (0,_nestjs_common__rspack_import_3.Optional)()),
4081
+ _ts_param(1, (0,_nestjs_common__rspack_import_3.Inject)(_flusys_nestjs_shared_interfaces__rspack_import_2.PERMISSION_SYNC_ADAPTER)),
3902
4082
  _ts_param(2, (0,_nestjs_common__rspack_import_3.Optional)()),
3903
- _ts_param(2, (0,_nestjs_common__rspack_import_3.Inject)(_flusys_nestjs_shared_interfaces__rspack_import_2.PERMISSION_SYNC_ADAPTER)),
4083
+ _ts_param(2, (0,_nestjs_common__rspack_import_3.Inject)('CACHE_INSTANCE')),
3904
4084
  _ts_metadata("design:type", Function),
3905
4085
  _ts_metadata("design:paramtypes", [
3906
4086
  typeof _auth_datasource_provider_js__rspack_import_8/* .AuthDataSourceProvider */.v === "undefined" ? Object : _auth_datasource_provider_js__rspack_import_8/* .AuthDataSourceProvider */.v,
3907
- typeof _auth_config_service_js__rspack_import_7/* .AuthConfigService */.W === "undefined" ? Object : _auth_config_service_js__rspack_import_7/* .AuthConfigService */.W,
3908
- typeof IPermissionSyncAdapter === "undefined" ? Object : IPermissionSyncAdapter
4087
+ typeof IPermissionSyncAdapter === "undefined" ? Object : IPermissionSyncAdapter,
4088
+ typeof HybridCache === "undefined" ? Object : HybridCache
3909
4089
  ])
3910
4090
  ], UserPermissionService);
3911
4091
 
@@ -3925,7 +4105,7 @@ __webpack_require__.d(__webpack_exports__, {
3925
4105
  /* import */ var _config_index_js__rspack_import_12 = __webpack_require__(7987);
3926
4106
  /* import */ var _config_index_js__rspack_import_13 = __webpack_require__(5363);
3927
4107
  /* import */ var _config_index_js__rspack_import_15 = __webpack_require__(5588);
3928
- /* import */ var _helpers_index_js__rspack_import_6 = __webpack_require__(334);
4108
+ /* import */ var _helpers_index_js__rspack_import_6 = __webpack_require__(5634);
3929
4109
  /* import */ var _dtos_user_dto_js__rspack_import_7 = __webpack_require__(7265);
3930
4110
  /* import */ var _entities_app_user_entity_js__rspack_import_8 = __webpack_require__(4758);
3931
4111
  /* import */ var _entities_user_company_permission_entity_js__rspack_import_9 = __webpack_require__(2377);
@@ -3992,6 +4172,19 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
3992
4172
  this.dataSource = await this.getDataSourceProvider().getDataSource();
3993
4173
  }
3994
4174
  }
4175
+ // The company-scoped list reads each user's grant (companyIsActive, membership)
4176
+ cacheDependencies() {
4177
+ return [
4178
+ this.entityName,
4179
+ _config_index_js__rspack_import_12/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION
4180
+ ];
4181
+ }
4182
+ async invalidateUserAndGrants() {
4183
+ await Promise.all([
4184
+ this.invalidateCache(),
4185
+ this.utilsService.clearCache(_config_index_js__rspack_import_12/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION, this.cacheManager, this.cacheTenant())
4186
+ ]);
4187
+ }
3995
4188
  async getUserForProfile(userId, user) {
3996
4189
  if (userId !== user.id) {
3997
4190
  throw new _nestjs_common__rspack_import_4.ForbiddenException({
@@ -4018,32 +4211,24 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
4018
4211
  await this.repository.update(id, {
4019
4212
  emailVerified: true
4020
4213
  });
4021
- await Promise.all([
4022
- this.clearCacheForAll(),
4023
- this.clearCacheForId([
4024
- {
4025
- id
4026
- }
4027
- ])
4028
- ]);
4214
+ await this.invalidateCache();
4029
4215
  }
4030
4216
  async verifyPhone(id) {
4031
4217
  await this.ensureDataSourceRepository();
4032
4218
  await this.repository.update(id, {
4033
4219
  phoneVerified: true
4034
4220
  });
4035
- await Promise.all([
4036
- this.clearCacheForAll(),
4037
- this.clearCacheForId([
4038
- {
4039
- id
4040
- }
4041
- ])
4042
- ]);
4221
+ await this.invalidateCache();
4043
4222
  }
4044
4223
  async updateStatus(id, isActive, user) {
4045
4224
  await this.ensureDataSourceRepository();
4046
4225
  const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
4226
+ if (isCompanyFeatureEnabled && user && !user.companyId) {
4227
+ throw new _nestjs_common__rspack_import_4.ForbiddenException({
4228
+ message: 'Select a company before changing a user status',
4229
+ messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.COMPANY_REQUIRED */.mK.COMPANY_REQUIRED
4230
+ });
4231
+ }
4047
4232
  if (isCompanyFeatureEnabled && user?.companyId && this.permissionRepository) {
4048
4233
  const permission = await this.permissionRepository.findOne({
4049
4234
  where: {
@@ -4069,14 +4254,7 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
4069
4254
  });
4070
4255
  }
4071
4256
  if (!isActive) await (0,_helpers_index_js__rspack_import_6/* .revokeUserSessions */.XP)(this.cacheManager, this.authConfig, id);
4072
- await Promise.all([
4073
- this.clearCacheForAll(),
4074
- this.clearCacheForId([
4075
- {
4076
- id
4077
- }
4078
- ])
4079
- ]);
4257
+ await this.invalidateUserAndGrants();
4080
4258
  }
4081
4259
  async convertSingleDtoToEntity(dto, user) {
4082
4260
  const entity = await super.convertSingleDtoToEntity(dto, user);
@@ -4211,6 +4389,10 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
4211
4389
  return superQuery;
4212
4390
  }
4213
4391
  // --- Company feature path ---
4392
+ if (user && !user.companyId) {
4393
+ query.andWhere('1 = 0');
4394
+ return superQuery;
4395
+ }
4214
4396
  if (!user?.companyId || !this.permissionRepository) {
4215
4397
  return superQuery;
4216
4398
  }
@@ -4297,12 +4479,7 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
4297
4479
  }
4298
4480
  }
4299
4481
  await queryRunner.commitTransaction();
4300
- await Promise.all([
4301
- this.clearCacheForAll(),
4302
- this.clearCacheForId([
4303
- savedUser
4304
- ])
4305
- ]);
4482
+ await this.invalidateUserAndGrants();
4306
4483
  return this.convertEntityToResponseDto(savedUser, false);
4307
4484
  } catch (error) {
4308
4485
  await queryRunner.rollbackTransaction();
@@ -4331,6 +4508,10 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
4331
4508
  total: result.total
4332
4509
  };
4333
4510
  }
4511
+ // The actionCode filter reads IAM grants, whose writes never move this cache's stamps
4512
+ bypassListCache(filterAndPaginationDto) {
4513
+ return !!filterAndPaginationDto?.filter?.actionCode;
4514
+ }
4334
4515
  async getEnrichedList(users, user) {
4335
4516
  if (this.userEnricher?.enrichListItems) {
4336
4517
  return this.userEnricher.enrichListItems(users, user);
@@ -4370,12 +4551,7 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
4370
4551
  try {
4371
4552
  await this.userEnricher.updateProfileSection(userId, sectionId, data, queryRunner);
4372
4553
  await queryRunner.commitTransaction();
4373
- await Promise.all([
4374
- this.clearCacheForAll(),
4375
- this.clearCacheForId([
4376
- userEntity
4377
- ])
4378
- ]);
4554
+ await this.invalidateCache();
4379
4555
  } catch (error) {
4380
4556
  await queryRunner.rollbackTransaction();
4381
4557
  throw error;
@@ -4391,6 +4567,12 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
4391
4567
  return 100;
4392
4568
  }
4393
4569
  async profile(dto, user) {
4570
+ if (dto.id && dto.id !== user.id) {
4571
+ throw new _nestjs_common__rspack_import_4.ForbiddenException({
4572
+ message: "Cannot access another user's profile",
4573
+ messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PROFILE_ACCESS_DENIED */.mK.PROFILE_ACCESS_DENIED
4574
+ });
4575
+ }
4394
4576
  await this.ensureDataSourceRepository();
4395
4577
  if (!this.dataSource) {
4396
4578
  throw new _nestjs_common__rspack_import_4.BadRequestException({
@@ -4398,7 +4580,7 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
4398
4580
  messageKey: _flusys_nestjs_shared_constants__rspack_import_2.SYSTEM_MESSAGES.DATASOURCE_NOT_AVAILABLE
4399
4581
  });
4400
4582
  }
4401
- const userId = dto.id ?? user.id;
4583
+ const userId = user.id;
4402
4584
  if (dto.extras && this.userEnricher?.validateProfileExtras) {
4403
4585
  await this.userEnricher.validateProfileExtras(userId, dto.extras, user);
4404
4586
  }
@@ -4418,37 +4600,29 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
4418
4600
  });
4419
4601
  }
4420
4602
  if (dto.password) {
4421
- const isOwnProfile = !dto.id || dto.id === user.id;
4422
- if (isOwnProfile) {
4423
- if (!dto.oldPassword) {
4424
- throw new _nestjs_common__rspack_import_4.BadRequestException({
4425
- message: 'Current password is required to change your password',
4426
- messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_CHANGE_CURRENT_REQUIRED */.mK.PASSWORD_CHANGE_CURRENT_REQUIRED
4427
- });
4428
- }
4429
- const isPasswordValid = await bcrypt__rspack_import_5.compare(dto.oldPassword, findUser.password);
4430
- if (!isPasswordValid) {
4431
- throw new _nestjs_common__rspack_import_4.BadRequestException({
4432
- message: 'Current password is incorrect',
4433
- messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_CHANGE_INVALID_CURRENT */.mK.PASSWORD_CHANGE_INVALID_CURRENT
4434
- });
4435
- }
4603
+ if (!dto.oldPassword) {
4604
+ throw new _nestjs_common__rspack_import_4.BadRequestException({
4605
+ message: 'Current password is required to change your password',
4606
+ messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_CHANGE_CURRENT_REQUIRED */.mK.PASSWORD_CHANGE_CURRENT_REQUIRED
4607
+ });
4608
+ }
4609
+ if (!findUser.password || !await bcrypt__rspack_import_5.compare(dto.oldPassword, findUser.password)) {
4610
+ throw new _nestjs_common__rspack_import_4.BadRequestException({
4611
+ message: 'Current password is incorrect',
4612
+ messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_CHANGE_INVALID_CURRENT */.mK.PASSWORD_CHANGE_INVALID_CURRENT
4613
+ });
4436
4614
  }
4437
4615
  findUser.password = await bcrypt__rspack_import_5.hash(dto.password, (/* inlined export .BCRYPT_SALT_ROUNDS */12));
4438
4616
  }
4439
- const { password, oldPassword, extras, emailVerified, phoneVerified, isActive, ...restDto } = dto;
4617
+ const { id, password, oldPassword, extras, emailVerified, phoneVerified, isActive, ...restDto } = dto;
4618
+ if (restDto.email && restDto.email !== findUser.email) findUser.emailVerified = false;
4440
4619
  Object.assign(findUser, restDto);
4441
4620
  await queryRunner.manager.save(this.repository.target, findUser);
4442
4621
  if (extras && this.userEnricher?.updateProfileExtras) {
4443
4622
  await this.userEnricher.updateProfileExtras(userId, extras, queryRunner);
4444
4623
  }
4445
4624
  await queryRunner.commitTransaction();
4446
- await Promise.all([
4447
- this.clearCacheForAll(),
4448
- this.clearCacheForId([
4449
- findUser
4450
- ])
4451
- ]);
4625
+ await this.invalidateCache();
4452
4626
  return findUser.id;
4453
4627
  } catch (error) {
4454
4628
  await queryRunner.rollbackTransaction();