najm-auth 3.4.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -659,6 +659,11 @@ interface SessionCookieData {
659
659
  * (password change/reset, logout-all) without hitting the database.
660
660
  */
661
661
  sessionVersion: number;
662
+ /**
663
+ * The refresh session this snapshot belongs to, so a single-device logout —
664
+ * which deliberately leaves the per-user version alone — can still reach it.
665
+ */
666
+ tokenFamily: string;
662
667
  /** Epoch ms when the cookie was written */
663
668
  iat: number;
664
669
  }
@@ -677,8 +682,10 @@ declare class CookieManager {
677
682
  getCookieName(): string;
678
683
  /**
679
684
  * Write a signed session cookie containing user data, roles, and permissions.
680
- * The cookie is HMAC-signed with the configured session secret so it is tamper-proof
681
- * but readable without a database query. Short TTL (5 min) ensures freshness.
685
+ * The cookie is HMAC-signed with the configured session secret, so its claims
686
+ * can be parsed without a database query. Authorization still verifies the
687
+ * session version and positive family liveness; a valid signature alone is
688
+ * never a revocation check. Short TTL (5 min) bounds claim staleness.
682
689
  */
683
690
  setSessionCookie(data: Omit<SessionCookieData, 'iat'>): void;
684
691
  /**
@@ -715,6 +722,12 @@ declare class UserRepository {
715
722
  role?: string | null;
716
723
  }) | undefined>;
717
724
  create(data: NewUser): Promise<User>;
725
+ /**
726
+ * Ids of everyone holding a role. Used to end sessions when the role's
727
+ * permission set changes: tokens carry permissions as claims, so the holders
728
+ * keep exercising the old set until their sessions do.
729
+ */
730
+ getIdsByRole(roleId: string): Promise<string[]>;
718
731
  update(id: string, data: Partial<NewUser>): Promise<User | undefined>;
719
732
  updateLastLogin(id: string): Promise<User>;
720
733
  incrementFailedAttempts(id: string): Promise<User>;
@@ -754,6 +767,7 @@ declare class UserValidator {
754
767
  * Check if user exists by email
755
768
  */
756
769
  checkUserExistsByEmail(email: string): Promise<{
770
+ password: string;
757
771
  id: string;
758
772
  name: string;
759
773
  createdAt: string;
@@ -762,9 +776,8 @@ declare class UserValidator {
762
776
  emailVerified: boolean;
763
777
  phone: string;
764
778
  phoneVerified: boolean;
765
- password: string;
766
779
  image: string;
767
- status: "active" | "inactive" | "pending";
780
+ status: "active" | "pending" | "inactive";
768
781
  roleId: string;
769
782
  lastLogin: string;
770
783
  failedLoginAttempts: number;
@@ -776,6 +789,7 @@ declare class UserValidator {
776
789
  * Check if email exists in database
777
790
  */
778
791
  checkEmailExists(email: string): Promise<{
792
+ password: string;
779
793
  id: string;
780
794
  name: string;
781
795
  createdAt: string;
@@ -784,9 +798,8 @@ declare class UserValidator {
784
798
  emailVerified: boolean;
785
799
  phone: string;
786
800
  phoneVerified: boolean;
787
- password: string;
788
801
  image: string;
789
- status: "active" | "inactive" | "pending";
802
+ status: "active" | "pending" | "inactive";
790
803
  roleId: string;
791
804
  lastLogin: string;
792
805
  failedLoginAttempts: number;
@@ -948,64 +961,6 @@ declare class RoleService {
948
961
  getRoleIdByName(name: any): Promise<string>;
949
962
  }
950
963
 
951
- type SanitizedUser = Omit<User, 'password' | 'failedLoginAttempts' | 'lockoutUntil'> & {
952
- role?: string | null;
953
- permissions?: string[];
954
- };
955
- declare class UserService {
956
- private roleValidator;
957
- private roleService;
958
- private userRepository;
959
- private userValidator;
960
- private encryptionService;
961
- private i18nService;
962
- private authConfig;
963
- private t;
964
- constructor(roleValidator: RoleValidator, roleService: RoleService, userRepository: UserRepository, userValidator: UserValidator, encryptionService: EncryptionService, i18nService: I18nService, authConfig: AuthConfig);
965
- private sanitizeUser;
966
- private sanitizeUsers;
967
- private requireUser;
968
- private resolveUserRole;
969
- getAll(options?: {
970
- limit?: number;
971
- offset?: number;
972
- }): Promise<SanitizedUser[]>;
973
- getById(id: string): Promise<SanitizedUser>;
974
- getByEmail(email: string): Promise<SanitizedUser>;
975
- /**
976
- * Find user by email without throwing - returns null if not found
977
- * Used for timing-safe authentication
978
- */
979
- findByEmail(email: string): Promise<(User & {
980
- role?: string | null;
981
- }) | undefined>;
982
- findByEmailInsensitive(email: string): Promise<(User & {
983
- role?: string | null;
984
- }) | undefined>;
985
- findByPhone(phone: string): Promise<UserWithPermissions>;
986
- getAuthRecordById(id: string): Promise<User | undefined>;
987
- create(data: Record<string, any>, options?: {
988
- validatePasswordStrength?: boolean;
989
- }): Promise<SanitizedUser>;
990
- update(id: string, data: Record<string, any>): Promise<SanitizedUser>;
991
- delete(id: string): Promise<SanitizedUser>;
992
- deleteAll(): Promise<SanitizedUser[]>;
993
- getRoleName(id: string): Promise<string | null>;
994
- updateLastLogin(id: string): Promise<void>;
995
- incrementFailedAttempts(id: string): Promise<number>;
996
- resetFailedAttempts(id: string): Promise<void>;
997
- setLockout(id: string, until: string): Promise<void>;
998
- assignRole(id: string, roleId?: string, roleName?: string): Promise<SanitizedUser>;
999
- removeRole(id: string): Promise<SanitizedUser>;
1000
- seedAdminUser(config?: {
1001
- email?: string;
1002
- password?: string;
1003
- name?: string;
1004
- }): Promise<SanitizedUser>;
1005
- updateLang(language: string): Promise<string>;
1006
- getLang(): Promise<string>;
1007
- }
1008
-
1009
964
  declare class TokenRepository {
1010
965
  db: TDb;
1011
966
  private schema;
@@ -1031,7 +986,7 @@ declare class TokenRepository {
1031
986
  }): Promise<any>;
1032
987
  /**
1033
988
  * Rotate an existing refresh-token family with compare-and-swap semantics.
1034
- * This can never insert a family deleted by a concurrent logout.
989
+ * This can never update a family durably revoked by a concurrent logout.
1035
990
  */
1036
991
  rotateRefreshToken(tokenData: {
1037
992
  userId: string;
@@ -1054,9 +1009,15 @@ declare class TokenRepository {
1054
1009
  markPreviousUsed(tokenFamily: string, previousHash: string): Promise<any>;
1055
1010
  /** Look up a single session's token row by its family identifier. */
1056
1011
  getByFamily(tokenFamily: string): Promise<any>;
1057
- /** Revoke a single session (one family). */
1012
+ /**
1013
+ * Durably revoke one family without depending on physical deletion.
1014
+ *
1015
+ * The row remains as a tombstone until its original refresh expiry. This is
1016
+ * what prevents a later cache loss from turning a failed/omitted cleanup
1017
+ * delete into a valid database-backed recovery session.
1018
+ */
1058
1019
  revokeFamily(tokenFamily: string): Promise<any>;
1059
- /** Revoke every session for a user (password change/reset, logout-all). */
1020
+ /** Durably revoke every active family for a user. */
1060
1021
  revokeAllForUser(userId: string): Promise<any>;
1061
1022
  /**
1062
1023
  * Opportunistic cleanup: with one row per family (no unique userId), expired
@@ -1073,6 +1034,193 @@ declare class TokenRepository {
1073
1034
  getUser(userId: string): Promise<any>;
1074
1035
  }
1075
1036
 
1037
+ /**
1038
+ * The single owner of "this credential is no longer good".
1039
+ *
1040
+ * Every path that changes a user's security state — status, password, role,
1041
+ * permissions, deletion — has to reach the same code, or one of them will be
1042
+ * forgotten and an already-issued token will outlive the change that was
1043
+ * supposed to end it. That is the shape of the defect this service closes.
1044
+ *
1045
+ * It deliberately depends on the repository rather than TokenService: TokenService
1046
+ * needs to call it too, and going through the service would close a DI cycle.
1047
+ */
1048
+ declare class SessionInvalidationService {
1049
+ private cache;
1050
+ private tokens;
1051
+ private config;
1052
+ constructor(cache: CacheService, tokens: TokenRepository);
1053
+ /**
1054
+ * Fields whose change ends existing sessions. Everything absent from this set
1055
+ * — display name, avatar, language — is a profile edit and must leave the
1056
+ * user signed in, on every device.
1057
+ */
1058
+ static readonly SECURITY_FIELDS: readonly ["password", "status", "role", "roleId", "email", "emailVerified", "phone"];
1059
+ /** Whether an update payload touches anything that must end sessions. */
1060
+ static affectsSecurityState(data: Record<string, unknown> | null | undefined): boolean;
1061
+ private get accessTokenTtlMs();
1062
+ private get refreshTokenTtlMs();
1063
+ sessionVersionKey(userId: string): string;
1064
+ revokedFamilyKey(tokenFamily: string): string;
1065
+ /**
1066
+ * Positive liveness marker for one session family.
1067
+ *
1068
+ * The signed session snapshot is authorized against this rather than against
1069
+ * the absence of a revocation marker, so losing the cache cannot make a
1070
+ * logged-out family look valid again: with no marker the fast path simply
1071
+ * declines and the request falls through to the database-backed resolver.
1072
+ */
1073
+ familyKey(tokenFamily: string): string;
1074
+ userCacheKey(userId: string): string;
1075
+ parseSessionVersion(raw: string | null): number;
1076
+ getSessionVersion(userId: string): Promise<number>;
1077
+ /**
1078
+ * Extend the version key's lifetime without rewriting its value.
1079
+ *
1080
+ * Token issuance used to `set()` the version it had just read, which meant an
1081
+ * invalidation landing between that read and that write was silently undone —
1082
+ * the revoked version came back and the old tokens verified again. Only the
1083
+ * expiry is touched here, so a concurrent bump always survives.
1084
+ */
1085
+ touchSessionVersion(userId: string): Promise<void>;
1086
+ /**
1087
+ * Invalidate every access token already issued for a user, and drop the
1088
+ * cached user record so the next read sees the new state rather than a stale
1089
+ * snapshot that is merely truthy.
1090
+ *
1091
+ * The bump is an atomic increment, so concurrent invalidations cannot read
1092
+ * the same version and write the same successor back.
1093
+ */
1094
+ invalidateAccessTokens(userId: string): Promise<number>;
1095
+ dropUserCache(userId: string): Promise<void>;
1096
+ /**
1097
+ * End every session a user holds: access tokens by version, refresh sessions
1098
+ * by row, and each family's liveness marker.
1099
+ *
1100
+ * Callers run this AFTER their database mutation has committed. Running it
1101
+ * before would leave a window in which a concurrent login re-established a
1102
+ * session against the state the mutation was about to remove; running it
1103
+ * after a rollback merely signs the user out again, which is safe.
1104
+ */
1105
+ invalidateUser(userId: string): Promise<void>;
1106
+ /**
1107
+ * Record that a family is live, and whose it is. Called wherever the family's
1108
+ * refresh row is written.
1109
+ *
1110
+ * The marker stores the owning user rather than a bare flag so a reader can
1111
+ * confirm, in the same single lookup, that the family it was handed actually
1112
+ * belongs to the identity claiming it.
1113
+ *
1114
+ * Revocation always wins. A refresh that rotated its row, was descheduled,
1115
+ * and resumed after a logout would otherwise re-mark its family live and
1116
+ * hand the browser back the session it had just ended — the database row is
1117
+ * gone by then, but nothing on the fast path reads the database. So this
1118
+ * writes, then re-reads the revocation marker and withdraws the write if one
1119
+ * appeared. Combined with `markFamilyRevoked` setting the revocation marker
1120
+ * *before* clearing liveness, every interleaving of the two converges on
1121
+ * revoked: whichever of the pair observes the other, the liveness key ends
1122
+ * up deleted.
1123
+ *
1124
+ * @returns whether the family is live after this call.
1125
+ */
1126
+ markFamilyIssued(tokenFamily: string, userId: string): Promise<boolean>;
1127
+ /**
1128
+ * What one lookup can say about a family, in a single batched cache read.
1129
+ *
1130
+ * The three answers are deliberately distinct. `revoked` is authoritative and
1131
+ * must deny. `unknown` means the cache cannot vouch for the family — it was
1132
+ * evicted, or the cache was lost — and must send the caller to an
1133
+ * authoritative, database-backed check rather than being read either way.
1134
+ * Only `live` is a positive assertion, and only for the named user.
1135
+ */
1136
+ familyStatus(tokenFamily: string | undefined, userId?: string): Promise<'live' | 'revoked' | 'unknown'>;
1137
+ /**
1138
+ * Whether a family is positively known to be live, and — when a user is
1139
+ * given — to belong to that user.
1140
+ *
1141
+ * `false` means "not proven live" — revoked, mismatched, or simply not in
1142
+ * cache. Callers must treat it as a reason to fall back to an authoritative
1143
+ * check, never as proof of validity in the other direction.
1144
+ */
1145
+ isFamilyLive(tokenFamily: string, userId?: string): Promise<boolean>;
1146
+ private readMany;
1147
+ /**
1148
+ * Keep revocation through both credential lifetimes. If deleting the refresh
1149
+ * row fails, its still-valid cookie must remain denied after access expires.
1150
+ */
1151
+ markFamilyRevoked(tokenFamily: string): Promise<void>;
1152
+ isFamilyRevoked(tokenFamily: string): Promise<boolean>;
1153
+ }
1154
+
1155
+ type SanitizedUser = Omit<User, 'password' | 'failedLoginAttempts' | 'lockoutUntil'> & {
1156
+ role?: string | null;
1157
+ permissions?: string[];
1158
+ };
1159
+ declare class UserService {
1160
+ private roleValidator;
1161
+ private roleService;
1162
+ private userRepository;
1163
+ private userValidator;
1164
+ private encryptionService;
1165
+ private i18nService;
1166
+ private authConfig;
1167
+ private sessionInvalidation?;
1168
+ private t;
1169
+ constructor(roleValidator: RoleValidator, roleService: RoleService, userRepository: UserRepository, userValidator: UserValidator, encryptionService: EncryptionService, i18nService: I18nService, authConfig: AuthConfig, sessionInvalidation?: SessionInvalidationService);
1170
+ /**
1171
+ * End the user's sessions after a security-relevant mutation has committed.
1172
+ *
1173
+ * Deactivating, deleting, or re-roling an account through the generic
1174
+ * endpoints used to leave every already-issued token working until it
1175
+ * expired. Invalidation belongs here, next to the write, so no caller can
1176
+ * forget it — the application-level commands that already revoke keep doing
1177
+ * so, and a second call is harmless.
1178
+ */
1179
+ private invalidateSessions;
1180
+ private sanitizeUser;
1181
+ private sanitizeUsers;
1182
+ private requireUser;
1183
+ private resolveUserRole;
1184
+ getAll(options?: {
1185
+ limit?: number;
1186
+ offset?: number;
1187
+ }): Promise<SanitizedUser[]>;
1188
+ getById(id: string): Promise<SanitizedUser>;
1189
+ getByEmail(email: string): Promise<SanitizedUser>;
1190
+ /**
1191
+ * Find user by email without throwing - returns null if not found
1192
+ * Used for timing-safe authentication
1193
+ */
1194
+ findByEmail(email: string): Promise<(User & {
1195
+ role?: string | null;
1196
+ }) | undefined>;
1197
+ findByEmailInsensitive(email: string): Promise<(User & {
1198
+ role?: string | null;
1199
+ }) | undefined>;
1200
+ findByPhone(phone: string): Promise<UserWithPermissions>;
1201
+ getAuthRecordById(id: string): Promise<User | undefined>;
1202
+ create(data: Record<string, any>, options?: {
1203
+ validatePasswordStrength?: boolean;
1204
+ }): Promise<SanitizedUser>;
1205
+ update(id: string, data: Record<string, any>): Promise<SanitizedUser>;
1206
+ delete(id: string): Promise<SanitizedUser>;
1207
+ deleteAll(): Promise<SanitizedUser[]>;
1208
+ getRoleName(id: string): Promise<string | null>;
1209
+ updateLastLogin(id: string): Promise<void>;
1210
+ incrementFailedAttempts(id: string): Promise<number>;
1211
+ resetFailedAttempts(id: string): Promise<void>;
1212
+ setLockout(id: string, until: string): Promise<void>;
1213
+ assignRole(id: string, roleId?: string, roleName?: string): Promise<SanitizedUser>;
1214
+ removeRole(id: string): Promise<SanitizedUser>;
1215
+ seedAdminUser(config?: {
1216
+ email?: string;
1217
+ password?: string;
1218
+ name?: string;
1219
+ }): Promise<SanitizedUser>;
1220
+ updateLang(language: string): Promise<string>;
1221
+ getLang(): Promise<string>;
1222
+ }
1223
+
1076
1224
  declare class CredentialSetupRequirementRepository {
1077
1225
  private db;
1078
1226
  private schema;
@@ -1090,17 +1238,18 @@ declare class TokenService {
1090
1238
  private cookieManager;
1091
1239
  private cache;
1092
1240
  private credentialSetupRequirements?;
1241
+ private sessions?;
1093
1242
  private config;
1094
1243
  private t;
1095
- constructor(tokenRepository: TokenRepository, cookieManager: CookieManager, cache: CacheService, credentialSetupRequirements?: CredentialSetupRequirementRepository);
1244
+ constructor(tokenRepository: TokenRepository, cookieManager: CookieManager, cache: CacheService, credentialSetupRequirements?: CredentialSetupRequirementRepository, sessions?: SessionInvalidationService);
1245
+ /** The shared invalidation contract — see SessionInvalidationService. */
1246
+ private get invalidation();
1096
1247
  /**
1097
1248
  * Get blacklist key prefix
1098
1249
  */
1099
1250
  private get blacklistPrefix();
1100
1251
  private get resetTokenPrefix();
1101
- private get sessionVersionPrefix();
1102
1252
  private sessionVersionKey;
1103
- private accessTokenTtlMs;
1104
1253
  private expiresAt;
1105
1254
  private getCacheValues;
1106
1255
  private parseSessionVersion;
@@ -1117,6 +1266,7 @@ declare class TokenService {
1117
1266
  private static readonly PREVIOUS_GRACE_SECONDS;
1118
1267
  private clearRefreshSessionCookies;
1119
1268
  private rejectRefreshSession;
1269
+ private assertRefreshFamilyAllowed;
1120
1270
  private readRefreshSessionCookie;
1121
1271
  /**
1122
1272
  * Read the refresh cookie and return the userId it belongs to.
@@ -1143,8 +1293,20 @@ declare class TokenService {
1143
1293
  roles: any[];
1144
1294
  permissions: any;
1145
1295
  sessionVersion: number;
1296
+ tokenFamily: string;
1146
1297
  }>;
1147
1298
  getUser(auth: string): Promise<any>;
1299
+ /**
1300
+ * A user record is only an authorization if the account is still usable.
1301
+ *
1302
+ * A valid signature and an unrevoked version say the *token* is intact; they
1303
+ * say nothing about whether the account behind it was since deactivated or
1304
+ * deleted. Session invalidation is what normally ends such a token, but this
1305
+ * check is what makes a truthy cached record insufficient on its own — so a
1306
+ * missed invalidation, or a record filled into cache moments before the
1307
+ * change, still cannot authorize a request.
1308
+ */
1309
+ private requireActiveUser;
1148
1310
  getUserById(userId: string): Promise<any>;
1149
1311
  private hashToken;
1150
1312
  getTokenExpire(token: string): number | undefined;
@@ -1156,9 +1318,22 @@ declare class TokenService {
1156
1318
  * session was invalidated after it was written.
1157
1319
  */
1158
1320
  getSessionVersion(userId: string): Promise<number>;
1321
+ /**
1322
+ * Whether one session family is positively known to be live.
1323
+ *
1324
+ * `false` means "not proven live" — logged out, or simply not in cache — and
1325
+ * is a reason to fall back to an authoritative check, never on its own a
1326
+ * reason to treat a session as valid.
1327
+ */
1328
+ isSessionFamilyLive(tokenFamily: string | undefined, userId?: string): Promise<boolean>;
1159
1329
  /**
1160
1330
  * Generate access token with unique jti for blacklist support.
1161
1331
  * Includes roles/permissions for client-side RBAC/PBAC.
1332
+ *
1333
+ * The token only verifies while its `tokenFamily` is a live session — see
1334
+ * verifyAccessToken. A token minted without one, or for a family that has
1335
+ * been revoked, is refused by design rather than trusted on its signature.
1336
+ * Use generateTokens() to establish a family.
1162
1337
  */
1163
1338
  generateAccessToken(data: {
1164
1339
  userId: string;
@@ -1233,30 +1408,24 @@ declare class TokenService {
1233
1408
  revokeFamily(tokenFamily: string): Promise<any>;
1234
1409
  /**
1235
1410
  * Opportunistic cleanup of expired/abandoned sessions. With one row per
1236
- * family (no unique userId), abandoned logins would otherwise accumulate.
1411
+ * family (no unique userId), expired live rows and revocation tombstones
1412
+ * would otherwise accumulate.
1237
1413
  * Best-effort — never let cleanup failure break the calling flow.
1238
1414
  */
1239
1415
  deleteExpiredSessions(): Promise<void>;
1240
1416
  invalidateUserAccessTokens(userId: string): Promise<number>;
1241
1417
  getUserFromCookie(): Promise<any>;
1242
- private get revokedFamilyPrefix();
1243
1418
  private revokedFamilyKey;
1244
- /**
1245
- * Mark a family as revoked in cache for the access-token TTL, so every
1246
- * access token minted for that family (not just the presented one) is
1247
- * rejected by verifyAccessToken until it would have expired anyway.
1248
- */
1249
- private markFamilyRevoked;
1250
1419
  /**
1251
1420
  * Revoke only the suspect family — NOT the whole user. Bumping the global
1252
1421
  * per-user session version here would kill every device's access tokens on a
1253
- * single family's reuse detection. Instead drop the family's refresh row and
1254
- * mark the family revoked so its access tokens stop verifying.
1422
+ * single family's reuse detection. Instead durably mark the family revoked
1423
+ * so database recovery and its access tokens both stop verifying.
1255
1424
  */
1256
1425
  private revokeSuspectRefreshFamily;
1257
1426
  /**
1258
1427
  * Logout the CURRENT session only — blacklist the presented access token,
1259
- * mark its family revoked, and delete that family's refresh row. Other
1428
+ * mark its family revoked in cache and durably in the token row. Other
1260
1429
  * devices/sessions for the same user keep working. Use a password change or
1261
1430
  * reset (revoke-all) to terminate every session.
1262
1431
  *
@@ -1296,8 +1465,20 @@ declare class TokenService {
1296
1465
  userId: string;
1297
1466
  }>;
1298
1467
  /**
1299
- * Verify password reset token
1300
- * Returns userId if valid, throws error if expired/invalid
1468
+ * Verify and CONSUME a password reset or invite token.
1469
+ *
1470
+ * Consumption is a single atomic compare-and-delete, so exactly one of any
1471
+ * number of concurrent callers holding the same link is told to proceed. The
1472
+ * earlier `get()` then `del()` pair left a window in which two callers both
1473
+ * read the same jti, both passed, and both went on to set a password.
1474
+ *
1475
+ * The comparison also means a stale token cannot delete the jti of a newer
1476
+ * one that superseded it — the newer link keeps working.
1477
+ *
1478
+ * Callers must finish validating the replacement password BEFORE calling
1479
+ * this: consumption is deliberately irreversible, so a token burned by a
1480
+ * request that then failed validation would cost the user their link for
1481
+ * nothing.
1301
1482
  */
1302
1483
  verifyResetToken(token: string): Promise<string>;
1303
1484
  private getUserSessionVersion;
@@ -1312,8 +1493,8 @@ declare const createUserDto: z.ZodObject<{
1312
1493
  emailVerified: z.ZodDefault<z.ZodBoolean>;
1313
1494
  status: z.ZodOptional<z.ZodEnum<{
1314
1495
  active: "active";
1315
- inactive: "inactive";
1316
1496
  pending: "pending";
1497
+ inactive: "inactive";
1317
1498
  }>>;
1318
1499
  }, z.core.$strip>;
1319
1500
  declare const updateUserDto: z.ZodObject<{
@@ -1325,8 +1506,8 @@ declare const updateUserDto: z.ZodObject<{
1325
1506
  emailVerified: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
1326
1507
  status: z.ZodOptional<z.ZodOptional<z.ZodEnum<{
1327
1508
  active: "active";
1328
- inactive: "inactive";
1329
1509
  pending: "pending";
1510
+ inactive: "inactive";
1330
1511
  }>>>;
1331
1512
  }, z.core.$strip>;
1332
1513
  declare const registerDto: z.ZodObject<{
@@ -1696,19 +1877,24 @@ declare class AuthService {
1696
1877
  }
1697
1878
 
1698
1879
  /**
1699
- * Composite key: resolved client address + hashed normalized identity.
1700
- * Buckets rate limits per client+credential combo so different users
1701
- * on the same address (e.g. localhost, NAT) don't share a single bucket.
1702
- *
1703
- * The address arrives already resolved through the configured trusted-proxy
1704
- * boundary; this module must never parse forwarding headers itself.
1880
+ * Login's bucket. `loginDto` accepts either field, and the service reads
1881
+ * `identifier` first, so the key follows the same order.
1705
1882
  */
1706
1883
  declare const authIdentityRateLimitKey: (ctx: Context, keyContext?: RateLimitKeyContext) => Promise<string>;
1884
+ /**
1885
+ * Bucket for routes whose DTO declares `email` and nothing else — password
1886
+ * reset requests and public registration. Deliberately does NOT fall back to
1887
+ * `identifier`: that field is discarded by validation, so honouring it would
1888
+ * hand a caller a fresh allowance per invented value while every request still
1889
+ * targeted the one email the handler reads.
1890
+ */
1891
+ declare const authEmailRateLimitKey: (ctx: Context, keyContext?: RateLimitKeyContext) => Promise<string>;
1707
1892
  declare class AuthController {
1708
1893
  private authService;
1709
1894
  constructor(authService: AuthService);
1710
1895
  loginUser(body: LoginDto): Promise<LoginResult>;
1711
1896
  inviteUser(body: InviteUserDto): Promise<Omit<{
1897
+ password: string;
1712
1898
  id: string;
1713
1899
  name: string;
1714
1900
  createdAt: string;
@@ -1717,9 +1903,8 @@ declare class AuthController {
1717
1903
  emailVerified: boolean;
1718
1904
  phone: string;
1719
1905
  phoneVerified: boolean;
1720
- password: string;
1721
1906
  image: string;
1722
- status: "active" | "inactive" | "pending";
1907
+ status: "active" | "pending" | "inactive";
1723
1908
  roleId: string;
1724
1909
  lastLogin: string;
1725
1910
  failedLoginAttempts: number;
@@ -1742,6 +1927,7 @@ declare class AuthController {
1742
1927
  message: string;
1743
1928
  }>;
1744
1929
  userProfile(authorization?: string): Promise<Omit<{
1930
+ password: string;
1745
1931
  id: string;
1746
1932
  name: string;
1747
1933
  createdAt: string;
@@ -1750,9 +1936,8 @@ declare class AuthController {
1750
1936
  emailVerified: boolean;
1751
1937
  phone: string;
1752
1938
  phoneVerified: boolean;
1753
- password: string;
1754
1939
  image: string;
1755
- status: "active" | "inactive" | "pending";
1940
+ status: "active" | "pending" | "inactive";
1756
1941
  roleId: string;
1757
1942
  lastLogin: string;
1758
1943
  failedLoginAttempts: number;
@@ -1772,6 +1957,14 @@ declare class AuthController {
1772
1957
  }
1773
1958
 
1774
1959
  declare class AuthGuard {
1960
+ /**
1961
+ * A resolved principal is not automatically an authorized one.
1962
+ *
1963
+ * The resolvers ahead of this guard already reject deactivated accounts, so
1964
+ * this is the backstop for anything that publishes a principal by another
1965
+ * route: a truthy user record must still be an active one to pass. Records
1966
+ * whose projection omits `status` are unchanged.
1967
+ */
1775
1968
  canActivate(user: any): boolean;
1776
1969
  }
1777
1970
  declare const isAuth: () => ClassDecorator & MethodDecorator;
@@ -2096,7 +2289,18 @@ declare class PermissionService {
2096
2289
  private permissionRepository;
2097
2290
  private permissionValidator;
2098
2291
  private roleService;
2099
- constructor(permissionRepository: PermissionRepository, permissionValidator: PermissionValidator, roleService: RoleService);
2292
+ private userRepository?;
2293
+ private sessionInvalidation?;
2294
+ constructor(permissionRepository: PermissionRepository, permissionValidator: PermissionValidator, roleService: RoleService, userRepository?: UserRepository, sessionInvalidation?: SessionInvalidationService);
2295
+ /**
2296
+ * End the sessions of everyone holding a role whose permission set changed.
2297
+ *
2298
+ * Access tokens and signed session snapshots both carry permissions as
2299
+ * claims, so a permission removed from a role stays exercisable until the
2300
+ * sessions that captured it end. This is an infrequent administrative
2301
+ * action, and the work is proportional to the role's membership.
2302
+ */
2303
+ private invalidateRoleHolders;
2100
2304
  getAll(): Promise<{
2101
2305
  id: string;
2102
2306
  name: string;
@@ -2833,4 +3037,4 @@ declare const authSeed: (config: AuthSeedConfig) => Record<string, SeedEntry>;
2833
3037
  */
2834
3038
  declare function seedAuthData(config: SeedAuthDataConfig): Promise<SeedAuthDataResult>;
2835
3039
 
2836
- export { AUTH_CONFIG, AUTH_CORE_MODULE, en as AUTH_EN, AUTH_LOCALES, AUTH_LOGIN_RATE_LIMIT_ENV, AUTH_MODULE, AUTH_PERMISSIONS, AUTH_ROLE, AUTH_SCHEMA, AUTH_SUPPORTED_LANGUAGES, AUTH_USER, type AssignPermissionDto, type AssignRoleDto, type AssignRoleParams, type AuthConfig, AuthController, AuthGuard, type AuthLoginRateLimitConfig, type AuthPluginConfig, AuthQueries, type AuthRateLimitEnvironment, AuthResolver, type AuthSchema, type AuthSeedConfig, AuthService, AuthSessionService, type AuthUser, CREDENTIAL_SETUP_CODES, CREDENTIAL_SETUP_MODULE, Can, CanCreate, CanDelete, CanList, CanRead, CanUpdate, type ChainableGuard, type ChangePasswordDto, type CheckPermissionDto, type ConfiguredOwnership, type ConfirmResetPasswordDto, CookieManager, type CreatePermissionDto, type CreateRoleDto, type CreateTokenDto, type CreateUserDto, type CredentialSetupChangeDto, type CredentialSetupCode, type CredentialSetupConfig, CredentialSetupController, type CredentialSetupOptions, type CredentialSetupPasswordOptions, type CredentialSetupPending, CredentialSetupRepository, CredentialSetupRequirementRepository, type CredentialSetupRequirementRow, CredentialSetupRequirementService, CredentialSetupService, type CredentialSetupSessionInfo, type CredentialSetupStarted, DEFAULT_AUTH_LOGIN_RATE_LIMIT, DEFAULT_CREDENTIAL_SETUP_COOKIE_NAME, DEFAULT_CREDENTIAL_SETUP_TTL_MS, type DefineRolesOptions, type EmailParam, EncryptionService, type GitHubOAuthConfig, type GoogleOAuthConfig, IdentityConfig, type IdentityResolver, type InviteUserDto, type JwtConfig, type JwtPayload, type LanguageParam, type LoginDto, type LoginResult, NewPermission, NewRoleEntity, NewUser, type OAuthConfig, type OAuthProvider, Owned, type OwnedMethods, type OwnershipConfig, type OwnershipProvider, type OwnershipRule, OwnershipToken, type OwnershipTokenOptions, PASSWORD_SETUP_PURPOSE, PUBLIC_REGISTRATION_MODULE, PasswordSetupService, Permission, PermissionController, PermissionGuard, type PermissionIdParam, PermissionRepository, PermissionService, PermissionValidator, Policy, type ProvisionUserInput, type ProvisionUserWithPasswordInput, type ProvisionUserWithSetupInput, ROLES, ROLE_GROUPS, type RefreshTokenDto, type RegisterDto, RegistrationController, type ResetPasswordDto, type ResolvedCredentialSetupConfig, ResolvedIdentityConfig, type ResourceAccessor, type ResourceGuards, type ResourceGuardsOptions, type RevokeTokenDto, Role, RoleController, RoleEntity, RoleGuard, type RoleIdParam, type RoleInput, RolePermission, RoleRepository, RoleService, type RoleType, RoleValidator, type RunAsUser, type SanitizedUser, ScopeContext, type ScopeResult, type SeedAuthDataConfig, type SeedAuthDataResult, type SeedUserConfig, type SessionCookieData, TOKEN_STATUS, TOKEN_TYPE, TemporaryCredentialInput, type TokenIdParam, type TokenPair, TokenRepository, TokenService, USER_STATUS, type UpdatePermissionDto, type UpdateRoleDto, type UpdateTokenDto, type UpdateUserDto, User, UserController, type UserIdInParam, type UserIdParam, type UserListQuery, UserRepository, UserService, UserValidator, type UserWithPermissions, type VerifyTokenDto, assignPermissionDto, assignRoleDto, assignRoleParams, auth$1 as auth, authIdentityRateLimitKey, authSeed, avatarsPath, calculateAge, calculateYearsOfExperience, changePasswordDto, checkPermissionDto, clean, configureOwnership, confirmResetPasswordDto, createIdentityResolver, createPermissionDto, createRoleDto, createTokenDto, createUserDto, credentialSetupChangeDto, credentialSetupError, defaultCredentialSetupPasswordSchema, defineRoles, emailParam, formatDate, getAuthLocale, getAvatarFile, inviteUserDto, isAdmin, isAdministrator, isAuth, isEmailIdentifier, isEmpty, isFile, isPath, join, languageParam, loginDto, normalizeAuthIdentifier, normalizeSetupPurpose, own, parseSchema, permissionIdParam, pickProps, refreshTokenDto, registerDto, resetPasswordDto, resolveAuthLoginRateLimitConfig, revokeTokenDto, roleIdParam, runAsUser, seedAuthData, tokenIdParam, updatePermissionDto, updateRoleDto, updateTokenDto, updateUserDto, userIdInParam, userIdParam, userListQuery, verifyTokenDto, where };
3040
+ export { AUTH_CONFIG, AUTH_CORE_MODULE, en as AUTH_EN, AUTH_LOCALES, AUTH_LOGIN_RATE_LIMIT_ENV, AUTH_MODULE, AUTH_PERMISSIONS, AUTH_ROLE, AUTH_SCHEMA, AUTH_SUPPORTED_LANGUAGES, AUTH_USER, type AssignPermissionDto, type AssignRoleDto, type AssignRoleParams, type AuthConfig, AuthController, AuthGuard, type AuthLoginRateLimitConfig, type AuthPluginConfig, AuthQueries, type AuthRateLimitEnvironment, AuthResolver, type AuthSchema, type AuthSeedConfig, AuthService, AuthSessionService, type AuthUser, CREDENTIAL_SETUP_CODES, CREDENTIAL_SETUP_MODULE, Can, CanCreate, CanDelete, CanList, CanRead, CanUpdate, type ChainableGuard, type ChangePasswordDto, type CheckPermissionDto, type ConfiguredOwnership, type ConfirmResetPasswordDto, CookieManager, type CreatePermissionDto, type CreateRoleDto, type CreateTokenDto, type CreateUserDto, type CredentialSetupChangeDto, type CredentialSetupCode, type CredentialSetupConfig, CredentialSetupController, type CredentialSetupOptions, type CredentialSetupPasswordOptions, type CredentialSetupPending, CredentialSetupRepository, CredentialSetupRequirementRepository, type CredentialSetupRequirementRow, CredentialSetupRequirementService, CredentialSetupService, type CredentialSetupSessionInfo, type CredentialSetupStarted, DEFAULT_AUTH_LOGIN_RATE_LIMIT, DEFAULT_CREDENTIAL_SETUP_COOKIE_NAME, DEFAULT_CREDENTIAL_SETUP_TTL_MS, type DefineRolesOptions, type EmailParam, EncryptionService, type GitHubOAuthConfig, type GoogleOAuthConfig, IdentityConfig, type IdentityResolver, type InviteUserDto, type JwtConfig, type JwtPayload, type LanguageParam, type LoginDto, type LoginResult, NewPermission, NewRoleEntity, NewUser, type OAuthConfig, type OAuthProvider, Owned, type OwnedMethods, type OwnershipConfig, type OwnershipProvider, type OwnershipRule, OwnershipToken, type OwnershipTokenOptions, PASSWORD_SETUP_PURPOSE, PUBLIC_REGISTRATION_MODULE, PasswordSetupService, Permission, PermissionController, PermissionGuard, type PermissionIdParam, PermissionRepository, PermissionService, PermissionValidator, Policy, type ProvisionUserInput, type ProvisionUserWithPasswordInput, type ProvisionUserWithSetupInput, ROLES, ROLE_GROUPS, type RefreshTokenDto, type RegisterDto, RegistrationController, type ResetPasswordDto, type ResolvedCredentialSetupConfig, ResolvedIdentityConfig, type ResourceAccessor, type ResourceGuards, type ResourceGuardsOptions, type RevokeTokenDto, Role, RoleController, RoleEntity, RoleGuard, type RoleIdParam, type RoleInput, RolePermission, RoleRepository, RoleService, type RoleType, RoleValidator, type RunAsUser, type SanitizedUser, ScopeContext, type ScopeResult, type SeedAuthDataConfig, type SeedAuthDataResult, type SeedUserConfig, type SessionCookieData, SessionInvalidationService, TOKEN_STATUS, TOKEN_TYPE, TemporaryCredentialInput, type TokenIdParam, type TokenPair, TokenRepository, TokenService, USER_STATUS, type UpdatePermissionDto, type UpdateRoleDto, type UpdateTokenDto, type UpdateUserDto, User, UserController, type UserIdInParam, type UserIdParam, type UserListQuery, UserRepository, UserService, UserValidator, type UserWithPermissions, type VerifyTokenDto, assignPermissionDto, assignRoleDto, assignRoleParams, auth$1 as auth, authEmailRateLimitKey, authIdentityRateLimitKey, authSeed, avatarsPath, calculateAge, calculateYearsOfExperience, changePasswordDto, checkPermissionDto, clean, configureOwnership, confirmResetPasswordDto, createIdentityResolver, createPermissionDto, createRoleDto, createTokenDto, createUserDto, credentialSetupChangeDto, credentialSetupError, defaultCredentialSetupPasswordSchema, defineRoles, emailParam, formatDate, getAuthLocale, getAvatarFile, inviteUserDto, isAdmin, isAdministrator, isAuth, isEmailIdentifier, isEmpty, isFile, isPath, join, languageParam, loginDto, normalizeAuthIdentifier, normalizeSetupPurpose, own, parseSchema, permissionIdParam, pickProps, refreshTokenDto, registerDto, resetPasswordDto, resolveAuthLoginRateLimitConfig, revokeTokenDto, roleIdParam, runAsUser, seedAuthData, tokenIdParam, updatePermissionDto, updateRoleDto, updateTokenDto, updateUserDto, userIdInParam, userIdParam, userListQuery, verifyTokenDto, where };