najm-auth 1.1.44 → 2.0.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/dist/index.js CHANGED
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/AuthPlugin.ts
9
- import { Err as Err8, plugin } from "najm-core";
9
+ import { Err as Err9, plugin } from "najm-core";
10
10
  import { cache } from "najm-cache";
11
11
 
12
12
  // src/auth.tokens.ts
@@ -290,6 +290,9 @@ var CookieManager = class CookieManager2 {
290
290
  get cookieName() {
291
291
  return this.config.refreshCookieName || "refreshToken";
292
292
  }
293
+ get refreshCookiePath() {
294
+ return this.config.refreshCookiePath || "/";
295
+ }
293
296
  get sessionCookieName() {
294
297
  return this.config.session.name;
295
298
  }
@@ -304,10 +307,10 @@ var CookieManager = class CookieManager2 {
304
307
  // =========================================================================
305
308
  setRefreshToken(refreshToken) {
306
309
  const maxAge = timestring(this.config.jwt.refreshExpiresIn, "s");
307
- this.cookieService.set(this.cookieName, refreshToken, { maxAge });
310
+ this.cookieService.set(this.cookieName, refreshToken, { maxAge, path: this.refreshCookiePath });
308
311
  }
309
312
  clearRefreshToken() {
310
- this.cookieService.delete(this.cookieName);
313
+ this.cookieService.delete(this.cookieName, { path: this.refreshCookiePath });
311
314
  }
312
315
  getRefreshToken() {
313
316
  return this.cookieService.get(this.cookieName);
@@ -373,14 +376,14 @@ CookieManager = __decorate2([
373
376
  ], CookieManager);
374
377
 
375
378
  // src/auth/AuthController.ts
376
- import { Controller as Controller2 } from "najm-core";
377
- import { Get as Get2, Post as Post2, ResMsg as ResMsg2 } from "najm-core";
378
- import { Body as Body2, User as User3, Headers } from "najm-core";
379
+ import { Controller } from "najm-core";
380
+ import { Get, Post, ResMsg } from "najm-core";
381
+ import { Body, User as User3, Headers } from "najm-core";
379
382
 
380
383
  // src/auth/AuthService.ts
381
384
  import { Injectable as Injectable7, Inject as Inject8 } from "najm-core";
382
- import { Err as Err6, Log } from "najm-core";
383
- import { I18n as I18n5, I18nService as I18nService2 } from "najm-i18n";
385
+ import { Err as Err7, Log } from "najm-core";
386
+ import { I18n as I18n6, I18nService as I18nService2 } from "najm-i18n";
384
387
  import { EmailService, passwordResetTemplate, accountInviteTemplate } from "najm-email";
385
388
  import { nanoid as nanoid5 } from "nanoid";
386
389
 
@@ -388,7 +391,7 @@ import { nanoid as nanoid5 } from "nanoid";
388
391
  import { Injectable as Injectable5, Inject as Inject5 } from "najm-core";
389
392
  import { Transaction } from "najm-database";
390
393
  import { I18nService } from "najm-i18n";
391
- import { I18n as I18n3 } from "najm-i18n";
394
+ import { I18n as I18n4 } from "najm-i18n";
392
395
 
393
396
  // src/users/UserRepository.ts
394
397
  import { eq as eq2, ne, sql as sql3 } from "drizzle-orm";
@@ -758,7 +761,8 @@ UserValidator = __decorate4([
758
761
  ], UserValidator);
759
762
 
760
763
  // src/roles/RoleService.ts
761
- import { Injectable as Injectable4 } from "najm-core";
764
+ import { Injectable as Injectable4, Err as Err3 } from "najm-core";
765
+ import { I18n as I18n3 } from "najm-i18n";
762
766
 
763
767
  // src/roles/RoleRepository.ts
764
768
  import { eq as eq3 } from "drizzle-orm";
@@ -782,6 +786,14 @@ var RoleRepository = class RoleRepository2 {
782
786
  get roles() {
783
787
  return this.schema.roles;
784
788
  }
789
+ get users() {
790
+ return this.schema.users;
791
+ }
792
+ /** True if any user currently references this role (blocks deletion). */
793
+ async hasUsers(roleId) {
794
+ const rows = await this.db.select({ id: this.users.id }).from(this.users).where(eq3(this.users.roleId, roleId)).limit(1);
795
+ return rows.length > 0;
796
+ }
785
797
  async getAll() {
786
798
  return await this.db.select().from(this.roles);
787
799
  }
@@ -917,6 +929,7 @@ var RoleService = class RoleService2 {
917
929
  }
918
930
  roleRepository;
919
931
  roleValidator;
932
+ t;
920
933
  constructor(roleRepository, roleValidator) {
921
934
  this.roleRepository = roleRepository;
922
935
  this.roleValidator = roleValidator;
@@ -936,12 +949,21 @@ var RoleService = class RoleService2 {
936
949
  return await this.roleRepository.create(data);
937
950
  }
938
951
  async update(id, data) {
939
- await this.roleValidator.checkRoleExists(id);
952
+ const role = await this.roleValidator.checkRoleExists(id);
953
+ if (role.name === ROLES.ADMIN && data.name && data.name !== ROLES.ADMIN) {
954
+ Err3(this.t("errors.cannotRenameSystem"), 403);
955
+ }
940
956
  await this.roleValidator.checkNameUnique(data.name, id);
941
957
  return await this.roleRepository.update(id, data);
942
958
  }
943
959
  async delete(id) {
944
- await this.roleValidator.checkRoleExists(id);
960
+ const role = await this.roleValidator.checkRoleExists(id);
961
+ if (role.name === ROLES.ADMIN) {
962
+ Err3(this.t("errors.cannotDeleteSystem"), 403);
963
+ }
964
+ if (await this.roleRepository.hasUsers(id)) {
965
+ Err3(this.t("errors.roleInUse"), 409);
966
+ }
945
967
  return await this.roleRepository.delete(id);
946
968
  }
947
969
  async seedDefaultRoles(defaultRoles) {
@@ -960,6 +982,10 @@ var RoleService = class RoleService2 {
960
982
  return role?.id;
961
983
  }
962
984
  };
985
+ __decorate7([
986
+ I18n3("roles"),
987
+ __metadata7("design:type", Object)
988
+ ], RoleService.prototype, "t", void 0);
963
989
  RoleService = __decorate7([
964
990
  Injectable4(),
965
991
  __metadata7("design:paramtypes", [typeof (_a4 = typeof RoleRepository !== "undefined" && RoleRepository) === "function" ? _a4 : Object, typeof (_b2 = typeof RoleValidator !== "undefined" && RoleValidator) === "function" ? _b2 : Object])
@@ -972,7 +998,7 @@ import { nanoid as nanoid3 } from "nanoid";
972
998
  import * as fs from "fs/promises";
973
999
  import * as path from "path";
974
1000
  import _isEmpty from "lodash.isempty";
975
- import { Err as Err3 } from "najm-core";
1001
+ import { Err as Err4 } from "najm-core";
976
1002
  var avatarsPath = path.join(process.cwd(), "avatars");
977
1003
  var parseSchema = /* @__PURE__ */ __name(async (schema, data) => {
978
1004
  try {
@@ -980,7 +1006,7 @@ var parseSchema = /* @__PURE__ */ __name(async (schema, data) => {
980
1006
  } catch (error) {
981
1007
  const errors = error.issues || error.errors || [];
982
1008
  const errorMessage = errors.map((err) => `${err.path.join(".")}: ${err.message}`).join("; ");
983
- Err3(errorMessage);
1009
+ Err4(errorMessage);
984
1010
  }
985
1011
  }, "parseSchema");
986
1012
  var clean = /* @__PURE__ */ __name((obj) => {
@@ -1066,7 +1092,7 @@ var isPath = /* @__PURE__ */ __name((img) => typeof img === "string" && img.trim
1066
1092
  var isFile = /* @__PURE__ */ __name((img) => !!img && typeof img !== "string" && img instanceof File, "isFile");
1067
1093
 
1068
1094
  // src/users/UserService.ts
1069
- import { Err as Err4 } from "najm-core";
1095
+ import { Err as Err5 } from "najm-core";
1070
1096
  var __decorate8 = function(decorators, target, key, desc) {
1071
1097
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1072
1098
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1121,7 +1147,7 @@ var UserService = class UserService2 {
1121
1147
  }
1122
1148
  requireUser(user) {
1123
1149
  if (!user) {
1124
- Err4(this.t("errors.notFound"), 404);
1150
+ Err5(this.t("errors.notFound"), 404);
1125
1151
  }
1126
1152
  return user;
1127
1153
  }
@@ -1135,12 +1161,12 @@ var UserService = class UserService2 {
1135
1161
  if (roleByName) {
1136
1162
  return roleByName.id;
1137
1163
  }
1138
- Err4(`Role '${roleName}' not found`);
1164
+ Err5(`Role '${roleName}' not found`);
1139
1165
  }
1140
1166
  if (this.authConfig.defaultRole) {
1141
1167
  const defaultRole = await this.roleService.getByName(this.authConfig.defaultRole);
1142
1168
  if (!defaultRole) {
1143
- Err4(`Default role '${this.authConfig.defaultRole}' not found. Create it first or set defaultRole to null in auth config.`);
1169
+ Err5(`Default role '${this.authConfig.defaultRole}' not found. Create it first or set defaultRole to null in auth config.`);
1144
1170
  }
1145
1171
  return defaultRole.id;
1146
1172
  }
@@ -1173,7 +1199,7 @@ var UserService = class UserService2 {
1173
1199
  async create(data) {
1174
1200
  const { id, email: email2, name, image, emailVerified, password, roleId, role } = data;
1175
1201
  if (!password || typeof password !== "string" || password.trim().length === 0) {
1176
- Err4("Password is required");
1202
+ Err5("Password is required");
1177
1203
  }
1178
1204
  this.userValidator.validatePasswordStrength(password);
1179
1205
  let userId = id || nanoid3(10);
@@ -1247,10 +1273,10 @@ var UserService = class UserService2 {
1247
1273
  }
1248
1274
  async seedAdminUser(config) {
1249
1275
  if (!config?.email || !config?.password) {
1250
- Err4("Admin email and password must be provided via config parameter");
1276
+ Err5("Admin email and password must be provided via config parameter");
1251
1277
  }
1252
1278
  if (config.password.length < 12) {
1253
- Err4("Admin password must be at least 12 characters");
1279
+ Err5("Admin password must be at least 12 characters");
1254
1280
  }
1255
1281
  const adminRole = await this.roleValidator.checkAdminRoleExists();
1256
1282
  const existingUser = await this.userRepository.getByEmail(config.email);
@@ -1279,7 +1305,7 @@ var UserService = class UserService2 {
1279
1305
  }
1280
1306
  };
1281
1307
  __decorate8([
1282
- I18n3("users"),
1308
+ I18n4("users"),
1283
1309
  __metadata8("design:type", Object)
1284
1310
  ], UserService.prototype, "t", void 0);
1285
1311
  __decorate8([
@@ -1296,7 +1322,7 @@ UserService = __decorate8([
1296
1322
 
1297
1323
  // src/tokens/TokenService.ts
1298
1324
  import { Injectable as Injectable6, Inject as Inject7 } from "najm-core";
1299
- import { I18n as I18n4 } from "najm-i18n";
1325
+ import { I18n as I18n5 } from "najm-i18n";
1300
1326
  import { CacheService } from "najm-cache";
1301
1327
  import { createHash } from "crypto";
1302
1328
  import jwt from "jsonwebtoken";
@@ -1413,7 +1439,7 @@ TokenRepository = __decorate9([
1413
1439
 
1414
1440
  // src/tokens/TokenService.ts
1415
1441
  import timestring2 from "timestring";
1416
- import { Err as Err5 } from "najm-core";
1442
+ import { Err as Err6 } from "najm-core";
1417
1443
  var __decorate10 = function(decorators, target, key, desc) {
1418
1444
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1419
1445
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1482,7 +1508,7 @@ var TokenService = class TokenService2 {
1482
1508
  if (authorization?.startsWith("Bearer ")) {
1483
1509
  return authorization.split(" ")[1];
1484
1510
  }
1485
- Err5(this.t("errors.tokenMissing"));
1511
+ Err6(this.t("errors.tokenMissing"));
1486
1512
  }
1487
1513
  /**
1488
1514
  * Verify access token and check blacklist
@@ -1493,7 +1519,7 @@ var TokenService = class TokenService2 {
1493
1519
  try {
1494
1520
  payload = jwt.verify(token, this.config.jwt.accessSecret);
1495
1521
  } catch {
1496
- Err5(this.t("errors.tokenVerificationFailed"));
1522
+ Err6(this.t("errors.tokenVerificationFailed"));
1497
1523
  }
1498
1524
  const sessionKey = this.sessionVersionKey(payload.userId);
1499
1525
  const blacklistKey = payload.jti ? `${this.blacklistPrefix}${payload.jti}` : null;
@@ -1506,15 +1532,15 @@ var TokenService = class TokenService2 {
1506
1532
  const values = await this.getCacheValues(keys);
1507
1533
  const valueByKey = new Map(keys.map((key, i) => [key, values[i]]));
1508
1534
  if (blacklistKey && valueByKey.get(blacklistKey) != null) {
1509
- Err5(this.t("errors.tokenRevoked"));
1535
+ Err6(this.t("errors.tokenRevoked"));
1510
1536
  }
1511
1537
  if (familyKey && valueByKey.get(familyKey) != null) {
1512
- Err5(this.t("errors.tokenRevoked"));
1538
+ Err6(this.t("errors.tokenRevoked"));
1513
1539
  }
1514
1540
  const activeSessionVersion = this.parseSessionVersion(valueByKey.get(sessionKey) ?? null);
1515
1541
  const tokenSessionVersion = payload.sessionVersion ?? 0;
1516
1542
  if (tokenSessionVersion !== activeSessionVersion) {
1517
- Err5(this.t("errors.tokenRevoked"));
1543
+ Err6(this.t("errors.tokenRevoked"));
1518
1544
  }
1519
1545
  return payload;
1520
1546
  }
@@ -1523,13 +1549,13 @@ var TokenService = class TokenService2 {
1523
1549
  try {
1524
1550
  decoded = jwt.verify(token, this.config.jwt.refreshSecret);
1525
1551
  } catch {
1526
- Err5(this.t("errors.tokenVerificationFailed"));
1552
+ Err6(this.t("errors.tokenVerificationFailed"));
1527
1553
  }
1528
1554
  if (decoded.type && decoded.type !== "refresh") {
1529
- Err5(this.t("errors.tokenVerificationFailed"));
1555
+ Err6(this.t("errors.tokenVerificationFailed"));
1530
1556
  }
1531
1557
  if (!decoded.tokenFamily) {
1532
- Err5(this.t("errors.tokenVerificationFailed"));
1558
+ Err6(this.t("errors.tokenVerificationFailed"));
1533
1559
  }
1534
1560
  return { userId: decoded.userId, tokenFamily: decoded.tokenFamily };
1535
1561
  }
@@ -1548,12 +1574,12 @@ var TokenService = class TokenService2 {
1548
1574
  async resolveUserFromCookie() {
1549
1575
  const refreshToken = this.cookieManager.getRefreshToken();
1550
1576
  if (!refreshToken) {
1551
- Err5(this.t("errors.refreshTokenMissing"));
1577
+ Err6(this.t("errors.refreshTokenMissing"));
1552
1578
  }
1553
1579
  const { userId, tokenFamily } = this.verifyRefreshToken(refreshToken);
1554
1580
  const stored = await this.tokenRepository.getByFamily(tokenFamily);
1555
1581
  if (!stored || stored.userId !== userId) {
1556
- Err5(this.t("errors.refreshTokenInvalid"));
1582
+ Err6(this.t("errors.refreshTokenInvalid"));
1557
1583
  }
1558
1584
  const presentedHash = this.hashToken(refreshToken);
1559
1585
  if (presentedHash === stored.token) {
@@ -1563,7 +1589,7 @@ var TokenService = class TokenService2 {
1563
1589
  if (canRecover) {
1564
1590
  return userId;
1565
1591
  }
1566
- Err5(this.t("errors.refreshTokenInvalid"));
1592
+ Err6(this.t("errors.refreshTokenInvalid"));
1567
1593
  }
1568
1594
  // ============ USER RETRIEVAL (MAIN METHOD) ============
1569
1595
  async getUser(auth2) {
@@ -1604,7 +1630,15 @@ var TokenService = class TokenService2 {
1604
1630
  await this.cache.set(this.sessionVersionKey(data.userId), String(sessionVersion), this.accessTokenTtlMs());
1605
1631
  }
1606
1632
  const token = jwt.sign({ ...data, jti, sessionVersion, exp: expiresAt }, this.config.jwt.accessSecret);
1607
- return { token, expiresAt };
1633
+ return { token, expiresAt, sessionVersion };
1634
+ }
1635
+ /**
1636
+ * Current per-user session version (0 when never invalidated). The signed
1637
+ * session cookie stamps this so a fast-path reader can reject a cookie whose
1638
+ * session was invalidated after it was written.
1639
+ */
1640
+ async getSessionVersion(userId) {
1641
+ return this.getUserSessionVersion(userId);
1608
1642
  }
1609
1643
  /**
1610
1644
  * Generate access token with unique jti for blacklist support.
@@ -1643,6 +1677,7 @@ var TokenService = class TokenService2 {
1643
1677
  tokenFamily: family,
1644
1678
  roles: accessTokenData.roles,
1645
1679
  permissions: accessTokenData.permissions,
1680
+ sessionVersion: access.sessionVersion,
1646
1681
  accessToken: access.token,
1647
1682
  refreshToken: refresh.token,
1648
1683
  accessTokenExpiresAt: access.expiresAt,
@@ -1710,12 +1745,12 @@ var TokenService = class TokenService2 {
1710
1745
  async refreshTokens() {
1711
1746
  const refreshToken = this.cookieManager.getRefreshToken();
1712
1747
  if (!refreshToken) {
1713
- Err5(this.t("errors.refreshTokenMissing"));
1748
+ Err6(this.t("errors.refreshTokenMissing"));
1714
1749
  }
1715
1750
  const { userId, tokenFamily } = this.verifyRefreshToken(refreshToken);
1716
1751
  const stored = await this.tokenRepository.getByFamily(tokenFamily);
1717
1752
  if (!stored || stored.userId !== userId) {
1718
- Err5(this.t("errors.refreshTokenInvalid"));
1753
+ Err6(this.t("errors.refreshTokenInvalid"));
1719
1754
  }
1720
1755
  const presentedHash = this.hashToken(refreshToken);
1721
1756
  if (presentedHash === stored.token) {
@@ -1725,12 +1760,12 @@ var TokenService = class TokenService2 {
1725
1760
  if (canRecover) {
1726
1761
  const claimed = await this.tokenRepository.markPreviousUsed(tokenFamily, presentedHash);
1727
1762
  if (!claimed?.length) {
1728
- Err5(this.t("errors.refreshTokenInvalid"));
1763
+ Err6(this.t("errors.refreshTokenInvalid"));
1729
1764
  }
1730
1765
  return this.generateTokens(userId, tokenFamily);
1731
1766
  }
1732
1767
  await this.revokeSuspectRefreshFamily(userId, tokenFamily);
1733
- Err5(this.t("errors.refreshTokenInvalid"));
1768
+ Err6(this.t("errors.refreshTokenInvalid"));
1734
1769
  }
1735
1770
  /** Revoke every refresh session for a user (password change/reset, logout-all). */
1736
1771
  async revokeAllForUser(userId) {
@@ -1762,7 +1797,7 @@ var TokenService = class TokenService2 {
1762
1797
  const userId = await this.resolveUserFromCookie();
1763
1798
  const user = await this.getUserById(userId);
1764
1799
  if (!user) {
1765
- Err5(this.t("errors.refreshTokenInvalid"));
1800
+ Err6(this.t("errors.refreshTokenInvalid"));
1766
1801
  }
1767
1802
  return user;
1768
1803
  }
@@ -1904,15 +1939,15 @@ var TokenService = class TokenService2 {
1904
1939
  try {
1905
1940
  decoded = jwt.verify(token, this.config.jwt.refreshSecret);
1906
1941
  } catch {
1907
- Err5(this.t("errors.resetTokenExpired"));
1942
+ Err6(this.t("errors.resetTokenExpired"));
1908
1943
  }
1909
1944
  if (decoded.type !== "reset" && decoded.type !== "invite" || !decoded.jti) {
1910
- Err5(this.t("errors.invalidResetToken"));
1945
+ Err6(this.t("errors.invalidResetToken"));
1911
1946
  }
1912
1947
  const key = `${this.resetTokenPrefix}${decoded.userId}`;
1913
1948
  const storedJti = await this.cache.get(key);
1914
1949
  if (!storedJti || storedJti !== decoded.jti) {
1915
- Err5(this.t("errors.invalidResetToken"));
1950
+ Err6(this.t("errors.invalidResetToken"));
1916
1951
  }
1917
1952
  await this.cache.del(key);
1918
1953
  return decoded.userId;
@@ -1926,7 +1961,7 @@ __decorate10([
1926
1961
  __metadata10("design:type", Object)
1927
1962
  ], TokenService.prototype, "config", void 0);
1928
1963
  __decorate10([
1929
- I18n4("auth"),
1964
+ I18n5("auth"),
1930
1965
  __metadata10("design:type", Object)
1931
1966
  ], TokenService.prototype, "t", void 0);
1932
1967
  TokenService = TokenService_1 = __decorate10([
@@ -1993,7 +2028,13 @@ var AuthService = class AuthService2 {
1993
2028
  await this.getDummyHash();
1994
2029
  }
1995
2030
  async registerUser(body) {
1996
- return await this.userService.create(body);
2031
+ return await this.userService.create({
2032
+ name: body.name,
2033
+ email: body.email,
2034
+ password: body.password,
2035
+ image: body.image,
2036
+ emailVerified: false
2037
+ });
1997
2038
  }
1998
2039
  /**
1999
2040
  * Admin-initiated account creation. The user is created with a random,
@@ -2019,15 +2060,17 @@ var AuthService = class AuthService2 {
2019
2060
  });
2020
2061
  const { token } = await this.tokenService.generateInviteToken(user.id);
2021
2062
  const inviteLink = `${this.config.frontendUrl}/reset-password?token=${token}`;
2063
+ let emailSent = false;
2022
2064
  try {
2023
2065
  await this.emailService.sendHtml(body.email, this.t("emails.accountInvite.subject"), accountInviteTemplate({
2024
2066
  inviteLink,
2025
2067
  userName: user.name || body.email
2026
2068
  }));
2069
+ emailSent = true;
2027
2070
  } catch (error) {
2028
2071
  this.logger.warn("Account invite email failed", { email: body.email, error });
2029
2072
  }
2030
- return user;
2073
+ return { ...user, emailSent };
2031
2074
  }
2032
2075
  /**
2033
2076
  * Create a login for a person record. The branch is intentional and is the
@@ -2062,7 +2105,7 @@ var AuthService = class AuthService2 {
2062
2105
  user.lockoutUntil = null;
2063
2106
  }
2064
2107
  if (user && this.isLockoutActive(user.lockoutUntil)) {
2065
- Err6(this.t("errors.accountLocked"), 423);
2108
+ Err7(this.t("errors.accountLocked"), 423);
2066
2109
  }
2067
2110
  const storedHash = user?.password ?? await this.getDummyHash();
2068
2111
  const isValid = await this.userValidator.comparePassword(password, storedHash);
@@ -2071,13 +2114,16 @@ var AuthService = class AuthService2 {
2071
2114
  const attempts = await this.userService.incrementFailedAttempts(user.id);
2072
2115
  if (attempts >= this.config.lockout.maxAttempts) {
2073
2116
  await this.userService.setLockout(user.id, this.nextLockoutUntil());
2074
- Err6(this.t("errors.accountLocked"), 423);
2117
+ Err7(this.t("errors.accountLocked"), 423);
2075
2118
  }
2076
2119
  }
2077
- Err6(this.t("errors.invalidCredentials"));
2120
+ Err7(this.t("errors.invalidCredentials"));
2078
2121
  }
2079
2122
  if (user.status !== "active") {
2080
- Err6(this.t("errors.accountInactive"));
2123
+ Err7(this.t("errors.accountInactive"));
2124
+ }
2125
+ if (this.config.requireVerifiedEmail && !user.emailVerified) {
2126
+ Err7(this.t("errors.emailNotVerified"), 403);
2081
2127
  }
2082
2128
  if ((user.failedLoginAttempts ?? 0) > 0 || user.lockoutUntil) {
2083
2129
  await this.userService.resetFailedAttempts(user.id);
@@ -2087,13 +2133,14 @@ var AuthService = class AuthService2 {
2087
2133
  this.cookieManager.setRefreshToken(generated.refreshToken);
2088
2134
  await this.userService.updateLastLogin(user.id);
2089
2135
  const { password: _, failedLoginAttempts: __, lockoutUntil: ___, ...sanitized } = user;
2090
- const { roles, permissions } = generated;
2136
+ const { roles, permissions, sessionVersion } = generated;
2091
2137
  this.cookieManager.setSessionCookie({
2092
2138
  user: { id: sanitized.id, email: sanitized.email, name: sanitized.name, role: sanitized.role, status: sanitized.status ?? void 0 },
2093
2139
  roles,
2094
- permissions
2140
+ permissions,
2141
+ sessionVersion
2095
2142
  });
2096
- const { userId: _userId, tokenFamily: _tokenFamily, roles: _roles, permissions: _permissions, ...tokens } = generated;
2143
+ const { userId: _userId, tokenFamily: _tokenFamily, roles: _roles, permissions: _permissions, sessionVersion: _sv, ...tokens } = generated;
2097
2144
  return { ...tokens, user: sanitized };
2098
2145
  }
2099
2146
  async refreshTokens() {
@@ -2104,10 +2151,11 @@ var AuthService = class AuthService2 {
2104
2151
  this.cookieManager.setSessionCookie({
2105
2152
  user: { id: user.id, email: user.email, name: user.name, role: user.role, status: user.status ?? void 0 },
2106
2153
  roles: generated.roles,
2107
- permissions: generated.permissions
2154
+ permissions: generated.permissions,
2155
+ sessionVersion: generated.sessionVersion
2108
2156
  });
2109
2157
  }
2110
- const { userId: _userId, tokenFamily: _tokenFamily, roles: _roles, permissions: _permissions, ...tokens } = generated;
2158
+ const { userId: _userId, tokenFamily: _tokenFamily, roles: _roles, permissions: _permissions, sessionVersion: _sv, ...tokens } = generated;
2111
2159
  return tokens;
2112
2160
  }
2113
2161
  async logoutUser(userId, authorization) {
@@ -2153,7 +2201,7 @@ var AuthService = class AuthService2 {
2153
2201
  const lang = this.i18nService.getCurrentLanguage();
2154
2202
  result = { ...user, language: lang };
2155
2203
  const token = this.tokenService.decodeAccessToken(authorization.replace(/^Bearer\s+/i, ""));
2156
- cachePayload = { roles: token?.roles ?? [], permissions: token?.permissions ?? [] };
2204
+ cachePayload = { roles: token?.roles ?? [], permissions: token?.permissions ?? [], sessionVersion: token?.sessionVersion ?? 0 };
2157
2205
  } else {
2158
2206
  result = await this.getUserFromCookie();
2159
2207
  }
@@ -2164,7 +2212,8 @@ var AuthService = class AuthService2 {
2164
2212
  this.cookieManager.setSessionCookie({
2165
2213
  user: { id: result.id, email: result.email, name: result.name, role: result.role, status: result.status ?? void 0 },
2166
2214
  roles: cachePayload.roles,
2167
- permissions: cachePayload.permissions
2215
+ permissions: cachePayload.permissions,
2216
+ sessionVersion: cachePayload.sessionVersion
2168
2217
  });
2169
2218
  }
2170
2219
  return result;
@@ -2188,11 +2237,11 @@ var AuthService = class AuthService2 {
2188
2237
  async changePassword(userId, currentPassword, newPassword) {
2189
2238
  const user = await this.userService.getAuthRecordById(userId);
2190
2239
  if (!user?.password) {
2191
- Err6(this.t("errors.invalidCredentials"));
2240
+ Err7(this.t("errors.invalidCredentials"));
2192
2241
  }
2193
2242
  const isValid = await this.userValidator.comparePassword(currentPassword, user.password);
2194
2243
  if (!isValid) {
2195
- Err6(this.t("errors.invalidCredentials"));
2244
+ Err7(this.t("errors.invalidCredentials"));
2196
2245
  }
2197
2246
  this.userValidator.validatePasswordStrength(newPassword);
2198
2247
  await this.userService.update(userId, { password: newPassword });
@@ -2218,7 +2267,7 @@ __decorate11([
2218
2267
  __metadata11("design:type", Object)
2219
2268
  ], AuthService.prototype, "config", void 0);
2220
2269
  __decorate11([
2221
- I18n5("auth"),
2270
+ I18n6("auth"),
2222
2271
  __metadata11("design:type", Object)
2223
2272
  ], AuthService.prototype, "t", void 0);
2224
2273
  __decorate11([
@@ -2266,29 +2315,6 @@ AuthGuard = __decorate12([
2266
2315
  ], AuthGuard);
2267
2316
  var isAuth = createGuard(AuthGuard);
2268
2317
 
2269
- // src/roles/index.ts
2270
- var roles_exports = {};
2271
- __export(roles_exports, {
2272
- ROLES: () => ROLES,
2273
- ROLE_GROUPS: () => ROLE_GROUPS,
2274
- Role: () => Role,
2275
- RoleController: () => RoleController,
2276
- RoleGuard: () => RoleGuard,
2277
- RoleRepository: () => RoleRepository,
2278
- RoleService: () => RoleService,
2279
- RoleValidator: () => RoleValidator,
2280
- assignRoleDto: () => assignRoleDto,
2281
- createRoleDto: () => createRoleDto,
2282
- defineRoles: () => defineRoles,
2283
- isAdmin: () => isAdmin,
2284
- isAdministrator: () => isAdministrator,
2285
- roleIdParam: () => roleIdParam,
2286
- updateRoleDto: () => updateRoleDto
2287
- });
2288
-
2289
- // src/roles/defineRoles.ts
2290
- import { composeGuards as composeGuards2, createGuard as createGuard3 } from "najm-guard";
2291
-
2292
2318
  // src/roles/RoleGuards.ts
2293
2319
  import { Service as Service3 } from "najm-core";
2294
2320
  import { GuardParams, User as User2 } from "najm-core";
@@ -2336,239 +2362,90 @@ var Role = createGuard2(RoleGuard);
2336
2362
  var isAdmin = composeGuards(isAuth(), Role(ROLES.ADMIN));
2337
2363
  var isAdministrator = composeGuards(isAuth(), Role(ROLE_GROUPS.ADMINISTRATORS));
2338
2364
 
2339
- // src/roles/defineRoles.ts
2340
- var Role2 = createGuard3(RoleGuard);
2341
- function defineRoles(roles, options) {
2342
- const ROLES2 = roles;
2343
- const superRoleKeys = options?.superRoles ?? [];
2344
- function resolveRoleValues(keys) {
2345
- return Array.from(new Set([...keys, ...superRoleKeys].map((key) => roles[key])));
2346
- }
2347
- __name(resolveRoleValues, "resolveRoleValues");
2348
- const guards2 = {};
2349
- for (const [key, value] of Object.entries(roles)) {
2350
- const name = `is${key.charAt(0).toUpperCase()}${key.slice(1).toLowerCase()}`;
2351
- const allowedValues = resolveRoleValues([key]);
2352
- guards2[name] = composeGuards2(isAuth(), Role2(allowedValues.length === 1 ? value : allowedValues));
2353
- }
2354
- function createGroupGuard(keys) {
2355
- const values = resolveRoleValues(keys);
2356
- return composeGuards2(isAuth(), Role2(values));
2357
- }
2358
- __name(createGroupGuard, "createGroupGuard");
2359
- function hasRole(userRole, ...keys) {
2360
- if (!userRole)
2361
- return false;
2362
- const normalized = userRole.toLowerCase();
2363
- return resolveRoleValues(keys).some((role) => role === normalized);
2364
- }
2365
- __name(hasRole, "hasRole");
2366
- function isInGroup(userRole, keys) {
2367
- return hasRole(userRole, ...keys);
2368
- }
2369
- __name(isInGroup, "isInGroup");
2370
- return { ROLES: ROLES2, createGroupGuard, hasRole, isInGroup, ...guards2 };
2371
- }
2372
- __name(defineRoles, "defineRoles");
2373
-
2374
- // src/roles/RoleController.ts
2375
- import { Controller } from "najm-core";
2376
- import { Get, Post, Put, Delete, ResMsg } from "najm-core";
2377
- import { Params, Body } from "najm-core";
2378
- import { Validate } from "najm-validation";
2379
-
2380
- // src/roles/RoleDto.ts
2381
- import { z } from "zod";
2382
- var nameField = z.string().min(2, "Name must be at least 2 characters").max(50, "Name too long");
2383
- var descriptionField = z.string().max(255, "Description too long").optional();
2384
- var createRoleDto = z.object({
2385
- name: nameField,
2386
- description: descriptionField
2387
- });
2388
- var updateRoleDto = createRoleDto.partial();
2389
- var roleIdParam = z.object({
2390
- id: z.string().length(5, "Role ID must be 5 characters")
2391
- });
2392
- var assignRoleDto = z.object({
2393
- userId: z.string().length(8, "User ID must be 8 characters"),
2394
- roleId: z.string().length(5, "Role ID must be 5 characters")
2395
- });
2396
-
2397
- // src/roles/RoleController.ts
2398
- var __decorate14 = function(decorators, target, key, desc) {
2399
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2400
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2401
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2402
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2403
- };
2404
- var __metadata14 = function(k, v) {
2405
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2406
- };
2407
- var __param5 = function(paramIndex, decorator) {
2408
- return function(target, key) {
2409
- decorator(target, key, paramIndex);
2410
- };
2411
- };
2412
- var _a8;
2413
- var RoleController = class RoleController2 {
2414
- static {
2415
- __name(this, "RoleController");
2416
- }
2417
- roleService;
2418
- constructor(roleService) {
2419
- this.roleService = roleService;
2420
- }
2421
- async getRoles() {
2422
- return this.roleService.getAll();
2423
- }
2424
- async getRole(params) {
2425
- return this.roleService.getById(params.id);
2426
- }
2427
- async createRole(body) {
2428
- return this.roleService.create(body);
2429
- }
2430
- async updateRole(params, body) {
2431
- return this.roleService.update(params.id, body);
2432
- }
2433
- async deleteRole(params) {
2434
- return this.roleService.delete(params.id);
2435
- }
2436
- };
2437
- __decorate14([
2438
- Get(),
2439
- isAdmin(),
2440
- ResMsg("roles.success.retrieved"),
2441
- __metadata14("design:type", Function),
2442
- __metadata14("design:paramtypes", []),
2443
- __metadata14("design:returntype", Promise)
2444
- ], RoleController.prototype, "getRoles", null);
2445
- __decorate14([
2446
- Get("/:id"),
2447
- isAdmin(),
2448
- Validate({ params: roleIdParam }),
2449
- ResMsg("roles.success.retrieved"),
2450
- __param5(0, Params()),
2451
- __metadata14("design:type", Function),
2452
- __metadata14("design:paramtypes", [Object]),
2453
- __metadata14("design:returntype", Promise)
2454
- ], RoleController.prototype, "getRole", null);
2455
- __decorate14([
2456
- Post(),
2457
- isAdmin(),
2458
- Validate(createRoleDto),
2459
- ResMsg("roles.success.created"),
2460
- __param5(0, Body()),
2461
- __metadata14("design:type", Function),
2462
- __metadata14("design:paramtypes", [Object]),
2463
- __metadata14("design:returntype", Promise)
2464
- ], RoleController.prototype, "createRole", null);
2465
- __decorate14([
2466
- Put("/:id"),
2467
- isAdmin(),
2468
- Validate({
2469
- params: roleIdParam,
2470
- body: updateRoleDto
2471
- }),
2472
- ResMsg("roles.success.updated"),
2473
- __param5(0, Params()),
2474
- __param5(1, Body()),
2475
- __metadata14("design:type", Function),
2476
- __metadata14("design:paramtypes", [Object, Object]),
2477
- __metadata14("design:returntype", Promise)
2478
- ], RoleController.prototype, "updateRole", null);
2479
- __decorate14([
2480
- Delete("/:id"),
2481
- isAdmin(),
2482
- Validate({ params: roleIdParam }),
2483
- ResMsg("roles.success.deleted"),
2484
- __param5(0, Params()),
2485
- __metadata14("design:type", Function),
2486
- __metadata14("design:paramtypes", [Object]),
2487
- __metadata14("design:returntype", Promise)
2488
- ], RoleController.prototype, "deleteRole", null);
2489
- RoleController = __decorate14([
2490
- Controller("/roles"),
2491
- __metadata14("design:paramtypes", [typeof (_a8 = typeof RoleService !== "undefined" && RoleService) === "function" ? _a8 : Object])
2492
- ], RoleController);
2493
-
2494
2365
  // src/auth/AuthController.ts
2495
- import { Validate as Validate2 } from "najm-validation";
2366
+ import { Validate } from "najm-validation";
2496
2367
  import { RateLimit } from "najm-rate";
2497
2368
  import { createHash as createHash2 } from "crypto";
2498
2369
 
2499
2370
  // src/users/UserDto.ts
2500
- import { z as z2 } from "zod";
2501
- var emailField = z2.string().email("Invalid email format");
2502
- var passwordField = z2.string().min(8, "Password must be at least 8 characters");
2503
- var optionalDateField = z2.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").nullable().optional();
2504
- var createUserDto = z2.object({
2505
- name: z2.string().max(100).optional(),
2371
+ import { z } from "zod";
2372
+ var emailField = z.string().email("Invalid email format");
2373
+ var passwordField = z.string().min(8, "Password must be at least 8 characters").refine((v) => new TextEncoder().encode(v).length <= 72, "Password must be at most 72 bytes").regex(/[A-Z]/, "Password must contain at least one uppercase letter").regex(/[a-z]/, "Password must contain at least one lowercase letter").regex(/\d/, "Password must contain at least one number");
2374
+ var optionalDateField = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").nullable().optional();
2375
+ var createUserDto = z.object({
2376
+ name: z.string().max(100).optional(),
2506
2377
  email: emailField,
2507
2378
  password: passwordField,
2508
- roleId: z2.string().min(1).optional(),
2509
- image: z2.string().nullish(),
2510
- emailVerified: z2.boolean().default(false),
2511
- status: z2.enum(["active", "inactive", "pending"]).optional()
2379
+ roleId: z.string().min(1).optional(),
2380
+ image: z.string().nullish(),
2381
+ emailVerified: z.boolean().default(false),
2382
+ status: z.enum(["active", "inactive", "pending"]).optional()
2512
2383
  });
2513
2384
  var updateUserDto = createUserDto.partial();
2514
- var inviteUserDto = z2.object({
2515
- name: z2.string().max(100).optional(),
2385
+ var registerDto = z.object({
2386
+ name: z.string().max(100).optional(),
2516
2387
  email: emailField,
2517
- roleId: z2.string().min(1).optional(),
2518
- image: z2.string().nullish()
2388
+ password: passwordField,
2389
+ image: z.string().nullish()
2519
2390
  });
2520
- var userIdParam = z2.object({
2521
- id: z2.string().min(1, "User ID is required")
2391
+ var inviteUserDto = z.object({
2392
+ name: z.string().max(100).optional(),
2393
+ email: emailField,
2394
+ roleId: z.string().min(1).optional(),
2395
+ image: z.string().nullish()
2522
2396
  });
2523
- var loginDto = z2.object({
2397
+ var userIdParam = z.object({
2398
+ id: z.string().min(1, "User ID is required")
2399
+ });
2400
+ var loginDto = z.object({
2524
2401
  email: emailField,
2525
2402
  password: passwordField
2526
2403
  });
2527
- var changePasswordDto = z2.object({
2404
+ var changePasswordDto = z.object({
2528
2405
  currentPassword: passwordField,
2529
2406
  newPassword: passwordField
2530
2407
  });
2531
- var resetPasswordDto = z2.object({
2408
+ var resetPasswordDto = z.object({
2532
2409
  email: emailField
2533
2410
  });
2534
- var confirmResetPasswordDto = z2.object({
2535
- token: z2.string().min(10, "Invalid reset token"),
2411
+ var confirmResetPasswordDto = z.object({
2412
+ token: z.string().min(10, "Invalid reset token"),
2536
2413
  newPassword: passwordField
2537
2414
  });
2538
- var languageParam = z2.object({
2539
- language: z2.string().min(2)
2415
+ var languageParam = z.object({
2416
+ language: z.string().min(2)
2540
2417
  });
2541
- var emailParam = z2.object({
2418
+ var emailParam = z.object({
2542
2419
  email: emailField
2543
2420
  });
2544
- var userIdInParam = z2.object({
2545
- userId: z2.string().min(1, "User ID is required")
2421
+ var userIdInParam = z.object({
2422
+ userId: z.string().min(1, "User ID is required")
2546
2423
  });
2547
- var assignRoleParams = z2.object({
2548
- userId: z2.string().min(1, "User ID is required"),
2549
- roleId: z2.string().min(1)
2424
+ var assignRoleParams = z.object({
2425
+ userId: z.string().min(1, "User ID is required"),
2426
+ roleId: z.string().min(1)
2550
2427
  });
2551
- var userListQuery = z2.object({
2552
- limit: z2.coerce.number().int().min(1).max(100).default(50),
2553
- offset: z2.coerce.number().int().min(0).default(0)
2428
+ var userListQuery = z.object({
2429
+ limit: z.coerce.number().int().min(1).max(100).default(50),
2430
+ offset: z.coerce.number().int().min(0).default(0)
2554
2431
  });
2555
2432
 
2556
2433
  // src/auth/AuthController.ts
2557
- var __decorate15 = function(decorators, target, key, desc) {
2434
+ var __decorate14 = function(decorators, target, key, desc) {
2558
2435
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2559
2436
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2560
2437
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2561
2438
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2562
2439
  };
2563
- var __metadata15 = function(k, v) {
2440
+ var __metadata14 = function(k, v) {
2564
2441
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2565
2442
  };
2566
- var __param6 = function(paramIndex, decorator) {
2443
+ var __param5 = function(paramIndex, decorator) {
2567
2444
  return function(target, key) {
2568
2445
  decorator(target, key, paramIndex);
2569
2446
  };
2570
2447
  };
2571
- var _a9;
2448
+ var _a8;
2572
2449
  var hashKeyPart = /* @__PURE__ */ __name((value) => createHash2("sha256").update(value).digest("base64url").slice(0, 32), "hashKeyPart");
2573
2450
  var cookieFingerprint = /* @__PURE__ */ __name(() => (ctx) => {
2574
2451
  const ip = ctx.req.header("x-forwarded-for")?.split(",")[0]?.trim() ?? ctx.req.header("x-real-ip") ?? "unknown";
@@ -2625,113 +2502,113 @@ var AuthController = class AuthController2 {
2625
2502
  return this.authService.resetPassword(body.token, body.newPassword);
2626
2503
  }
2627
2504
  };
2628
- __decorate15([
2629
- Post2("/register"),
2505
+ __decorate14([
2506
+ Post("/register"),
2630
2507
  RateLimit({ limit: 5, window: "15m", key: ipAndEmail }),
2631
- Validate2(createUserDto),
2632
- ResMsg2("auth.success.register"),
2633
- __param6(0, Body2()),
2634
- __metadata15("design:type", Function),
2635
- __metadata15("design:paramtypes", [Object]),
2636
- __metadata15("design:returntype", Promise)
2508
+ Validate(registerDto),
2509
+ ResMsg("auth.success.register"),
2510
+ __param5(0, Body()),
2511
+ __metadata14("design:type", Function),
2512
+ __metadata14("design:paramtypes", [Object]),
2513
+ __metadata14("design:returntype", Promise)
2637
2514
  ], AuthController.prototype, "registerUser", null);
2638
- __decorate15([
2639
- Post2("/login"),
2515
+ __decorate14([
2516
+ Post("/login"),
2640
2517
  RateLimit({ limit: 5, window: "15m", key: ipAndEmail, message: "Too many login attempts. Please try again later." }),
2641
- Validate2(loginDto),
2642
- ResMsg2("auth.success.login"),
2643
- __param6(0, Body2()),
2644
- __metadata15("design:type", Function),
2645
- __metadata15("design:paramtypes", [Object]),
2646
- __metadata15("design:returntype", Promise)
2518
+ Validate(loginDto),
2519
+ ResMsg("auth.success.login"),
2520
+ __param5(0, Body()),
2521
+ __metadata14("design:type", Function),
2522
+ __metadata14("design:paramtypes", [Object]),
2523
+ __metadata14("design:returntype", Promise)
2647
2524
  ], AuthController.prototype, "loginUser", null);
2648
- __decorate15([
2649
- Post2("/invite"),
2525
+ __decorate14([
2526
+ Post("/invite"),
2650
2527
  isAdmin(),
2651
2528
  RateLimit({ limit: 20, window: "15m", key: "user" }),
2652
- Validate2(inviteUserDto),
2653
- ResMsg2("auth.success.accountInviteSent"),
2654
- __param6(0, Body2()),
2655
- __metadata15("design:type", Function),
2656
- __metadata15("design:paramtypes", [Object]),
2657
- __metadata15("design:returntype", Promise)
2529
+ Validate(inviteUserDto),
2530
+ ResMsg("auth.success.accountInviteSent"),
2531
+ __param5(0, Body()),
2532
+ __metadata14("design:type", Function),
2533
+ __metadata14("design:paramtypes", [Object]),
2534
+ __metadata14("design:returntype", Promise)
2658
2535
  ], AuthController.prototype, "inviteUser", null);
2659
- __decorate15([
2660
- Post2("/refresh"),
2536
+ __decorate14([
2537
+ Post("/refresh"),
2661
2538
  RateLimit({ limit: 15, window: "15m", key: cookieFingerprint() }),
2662
- ResMsg2("auth.success.tokenRefreshed"),
2663
- __metadata15("design:type", Function),
2664
- __metadata15("design:paramtypes", []),
2665
- __metadata15("design:returntype", Promise)
2539
+ ResMsg("auth.success.tokenRefreshed"),
2540
+ __metadata14("design:type", Function),
2541
+ __metadata14("design:paramtypes", []),
2542
+ __metadata14("design:returntype", Promise)
2666
2543
  ], AuthController.prototype, "refreshTokens", null);
2667
- __decorate15([
2668
- Post2("/logout"),
2544
+ __decorate14([
2545
+ Post("/logout"),
2669
2546
  isAuth(),
2670
2547
  RateLimit({ limit: 10, window: "15m", key: "user" }),
2671
- __param6(0, User3("id")),
2672
- __param6(1, Headers("authorization")),
2673
- __metadata15("design:type", Function),
2674
- __metadata15("design:paramtypes", [String, String]),
2675
- __metadata15("design:returntype", Promise)
2548
+ __param5(0, User3("id")),
2549
+ __param5(1, Headers("authorization")),
2550
+ __metadata14("design:type", Function),
2551
+ __metadata14("design:paramtypes", [String, String]),
2552
+ __metadata14("design:returntype", Promise)
2676
2553
  ], AuthController.prototype, "logoutUser", null);
2677
- __decorate15([
2678
- Post2("/change-password"),
2554
+ __decorate14([
2555
+ Post("/change-password"),
2679
2556
  isAuth(),
2680
- Validate2(changePasswordDto),
2681
- ResMsg2("auth.success.passwordChanged"),
2682
- __param6(0, User3("id")),
2683
- __param6(1, Body2()),
2684
- __metadata15("design:type", Function),
2685
- __metadata15("design:paramtypes", [String, Object]),
2686
- __metadata15("design:returntype", Promise)
2557
+ Validate(changePasswordDto),
2558
+ ResMsg("auth.success.passwordChanged"),
2559
+ __param5(0, User3("id")),
2560
+ __param5(1, Body()),
2561
+ __metadata14("design:type", Function),
2562
+ __metadata14("design:paramtypes", [String, Object]),
2563
+ __metadata14("design:returntype", Promise)
2687
2564
  ], AuthController.prototype, "changePassword", null);
2688
- __decorate15([
2689
- Get2("/me"),
2565
+ __decorate14([
2566
+ Get("/me"),
2690
2567
  RateLimit({ limit: 30, window: "1m", key: cookieFingerprint() }),
2691
- ResMsg2("auth.users.success.retrieved"),
2692
- __param6(0, Headers("authorization")),
2693
- __metadata15("design:type", Function),
2694
- __metadata15("design:paramtypes", [String]),
2695
- __metadata15("design:returntype", Promise)
2568
+ ResMsg("auth.users.success.retrieved"),
2569
+ __param5(0, Headers("authorization")),
2570
+ __metadata14("design:type", Function),
2571
+ __metadata14("design:paramtypes", [String]),
2572
+ __metadata14("design:returntype", Promise)
2696
2573
  ], AuthController.prototype, "userProfile", null);
2697
- __decorate15([
2698
- Post2("/forgot-password"),
2574
+ __decorate14([
2575
+ Post("/forgot-password"),
2699
2576
  RateLimit({ limit: 3, window: "15m", key: ipAndEmail, message: "Too many password reset requests. Please try again later." }),
2700
- Validate2(resetPasswordDto),
2701
- ResMsg2("auth.success.passwordResetSent"),
2702
- __param6(0, Body2()),
2703
- __metadata15("design:type", Function),
2704
- __metadata15("design:paramtypes", [Object]),
2705
- __metadata15("design:returntype", Promise)
2577
+ Validate(resetPasswordDto),
2578
+ ResMsg("auth.success.passwordResetSent"),
2579
+ __param5(0, Body()),
2580
+ __metadata14("design:type", Function),
2581
+ __metadata14("design:paramtypes", [Object]),
2582
+ __metadata14("design:returntype", Promise)
2706
2583
  ], AuthController.prototype, "forgotPassword", null);
2707
- __decorate15([
2708
- Post2("/reset-password"),
2584
+ __decorate14([
2585
+ Post("/reset-password"),
2709
2586
  RateLimit({ limit: 5, window: "15m", key: "ip", message: "Too many password reset attempts. Please try again later." }),
2710
- Validate2(confirmResetPasswordDto),
2711
- ResMsg2("auth.success.passwordReset"),
2712
- __param6(0, Body2()),
2713
- __metadata15("design:type", Function),
2714
- __metadata15("design:paramtypes", [Object]),
2715
- __metadata15("design:returntype", Promise)
2587
+ Validate(confirmResetPasswordDto),
2588
+ ResMsg("auth.success.passwordReset"),
2589
+ __param5(0, Body()),
2590
+ __metadata14("design:type", Function),
2591
+ __metadata14("design:paramtypes", [Object]),
2592
+ __metadata14("design:returntype", Promise)
2716
2593
  ], AuthController.prototype, "resetPassword", null);
2717
- AuthController = __decorate15([
2718
- Controller2("/auth"),
2719
- __metadata15("design:paramtypes", [typeof (_a9 = typeof AuthService !== "undefined" && AuthService) === "function" ? _a9 : Object])
2594
+ AuthController = __decorate14([
2595
+ Controller("/auth"),
2596
+ __metadata14("design:paramtypes", [typeof (_a8 = typeof AuthService !== "undefined" && AuthService) === "function" ? _a8 : Object])
2720
2597
  ], AuthController);
2721
2598
 
2722
2599
  // src/auth/AuthResolver.ts
2723
2600
  import { APP, Container, DI, Inject as Inject9, LOGGER, Meta, Service as Service4 } from "najm-core";
2724
2601
  import { USER, ROLE, PERMISSIONS } from "najm-guard";
2725
- var __decorate16 = function(decorators, target, key, desc) {
2602
+ var __decorate15 = function(decorators, target, key, desc) {
2726
2603
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2727
2604
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2728
2605
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2729
2606
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2730
2607
  };
2731
- var __metadata16 = function(k, v) {
2608
+ var __metadata15 = function(k, v) {
2732
2609
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2733
2610
  };
2734
- var _a10;
2611
+ var _a9;
2735
2612
  var AuthResolver = class AuthResolver2 {
2736
2613
  static {
2737
2614
  __name(this, "AuthResolver");
@@ -2778,6 +2655,10 @@ var AuthResolver = class AuthResolver2 {
2778
2655
  const session = cookieManager.getSessionCookie();
2779
2656
  if (!session)
2780
2657
  return false;
2658
+ const tokenService = await this.container.resolve(TokenService);
2659
+ const currentVersion = await tokenService.getSessionVersion(session.user.id);
2660
+ if ((session.sessionVersion ?? 0) !== currentVersion)
2661
+ return false;
2781
2662
  return {
2782
2663
  user: { ...session.user, permissions: session.permissions },
2783
2664
  role: session.user.role ?? session.roles[0],
@@ -2843,19 +2724,19 @@ var AuthResolver = class AuthResolver2 {
2843
2724
  await authService.warmupPasswordHash();
2844
2725
  }
2845
2726
  };
2846
- __decorate16([
2727
+ __decorate15([
2847
2728
  DI(),
2848
- __metadata16("design:type", typeof (_a10 = typeof Container !== "undefined" && Container) === "function" ? _a10 : Object)
2729
+ __metadata15("design:type", typeof (_a9 = typeof Container !== "undefined" && Container) === "function" ? _a9 : Object)
2849
2730
  ], AuthResolver.prototype, "container", void 0);
2850
- __decorate16([
2731
+ __decorate15([
2851
2732
  Inject9(APP),
2852
- __metadata16("design:type", Object)
2733
+ __metadata15("design:type", Object)
2853
2734
  ], AuthResolver.prototype, "app", void 0);
2854
- __decorate16([
2735
+ __decorate15([
2855
2736
  Inject9(LOGGER),
2856
- __metadata16("design:type", Object)
2737
+ __metadata15("design:type", Object)
2857
2738
  ], AuthResolver.prototype, "log", void 0);
2858
- AuthResolver = __decorate16([
2739
+ AuthResolver = __decorate15([
2859
2740
  Service4(),
2860
2741
  Meta({ layer: "plugin", order: 30 })
2861
2742
  ], AuthResolver);
@@ -2908,6 +2789,7 @@ __export(users_exports, {
2908
2789
  inviteUserDto: () => inviteUserDto,
2909
2790
  languageParam: () => languageParam,
2910
2791
  loginDto: () => loginDto,
2792
+ registerDto: () => registerDto,
2911
2793
  resetPasswordDto: () => resetPasswordDto,
2912
2794
  updateUserDto: () => updateUserDto,
2913
2795
  userIdInParam: () => userIdInParam,
@@ -2919,6 +2801,184 @@ __export(users_exports, {
2919
2801
  import { Controller as Controller3 } from "najm-core";
2920
2802
  import { Get as Get3, Post as Post3, Put as Put2, Delete as Delete2, ResMsg as ResMsg3 } from "najm-core";
2921
2803
  import { Params as Params2, Body as Body3, Query } from "najm-core";
2804
+
2805
+ // src/roles/index.ts
2806
+ var roles_exports = {};
2807
+ __export(roles_exports, {
2808
+ ROLES: () => ROLES,
2809
+ ROLE_GROUPS: () => ROLE_GROUPS,
2810
+ Role: () => Role,
2811
+ RoleController: () => RoleController,
2812
+ RoleGuard: () => RoleGuard,
2813
+ RoleRepository: () => RoleRepository,
2814
+ RoleService: () => RoleService,
2815
+ RoleValidator: () => RoleValidator,
2816
+ assignRoleDto: () => assignRoleDto,
2817
+ createRoleDto: () => createRoleDto,
2818
+ defineRoles: () => defineRoles,
2819
+ isAdmin: () => isAdmin,
2820
+ isAdministrator: () => isAdministrator,
2821
+ roleIdParam: () => roleIdParam,
2822
+ updateRoleDto: () => updateRoleDto
2823
+ });
2824
+
2825
+ // src/roles/defineRoles.ts
2826
+ import { composeGuards as composeGuards2, createGuard as createGuard3 } from "najm-guard";
2827
+ var Role2 = createGuard3(RoleGuard);
2828
+ function defineRoles(roles, options) {
2829
+ const ROLES2 = roles;
2830
+ const superRoleKeys = options?.superRoles ?? [];
2831
+ function resolveRoleValues(keys) {
2832
+ return Array.from(new Set([...keys, ...superRoleKeys].map((key) => roles[key])));
2833
+ }
2834
+ __name(resolveRoleValues, "resolveRoleValues");
2835
+ const guards2 = {};
2836
+ for (const [key, value] of Object.entries(roles)) {
2837
+ const name = `is${key.charAt(0).toUpperCase()}${key.slice(1).toLowerCase()}`;
2838
+ const allowedValues = resolveRoleValues([key]);
2839
+ guards2[name] = composeGuards2(isAuth(), Role2(allowedValues.length === 1 ? value : allowedValues));
2840
+ }
2841
+ function createGroupGuard(keys) {
2842
+ const values = resolveRoleValues(keys);
2843
+ return composeGuards2(isAuth(), Role2(values));
2844
+ }
2845
+ __name(createGroupGuard, "createGroupGuard");
2846
+ function hasRole(userRole, ...keys) {
2847
+ if (!userRole)
2848
+ return false;
2849
+ const normalized = userRole.toLowerCase();
2850
+ return resolveRoleValues(keys).some((role) => role === normalized);
2851
+ }
2852
+ __name(hasRole, "hasRole");
2853
+ function isInGroup(userRole, keys) {
2854
+ return hasRole(userRole, ...keys);
2855
+ }
2856
+ __name(isInGroup, "isInGroup");
2857
+ return { ROLES: ROLES2, createGroupGuard, hasRole, isInGroup, ...guards2 };
2858
+ }
2859
+ __name(defineRoles, "defineRoles");
2860
+
2861
+ // src/roles/RoleController.ts
2862
+ import { Controller as Controller2 } from "najm-core";
2863
+ import { Get as Get2, Post as Post2, Put, Delete, ResMsg as ResMsg2 } from "najm-core";
2864
+ import { Params, Body as Body2 } from "najm-core";
2865
+ import { Validate as Validate2 } from "najm-validation";
2866
+
2867
+ // src/roles/RoleDto.ts
2868
+ import { z as z2 } from "zod";
2869
+ var nameField = z2.string().min(2, "Name must be at least 2 characters").max(50, "Name too long");
2870
+ var descriptionField = z2.string().max(255, "Description too long").optional();
2871
+ var createRoleDto = z2.object({
2872
+ name: nameField,
2873
+ description: descriptionField
2874
+ });
2875
+ var updateRoleDto = createRoleDto.partial();
2876
+ var roleIdParam = z2.object({
2877
+ id: z2.string().length(5, "Role ID must be 5 characters")
2878
+ });
2879
+ var assignRoleDto = z2.object({
2880
+ userId: z2.string().length(8, "User ID must be 8 characters"),
2881
+ roleId: z2.string().length(5, "Role ID must be 5 characters")
2882
+ });
2883
+
2884
+ // src/roles/RoleController.ts
2885
+ var __decorate16 = function(decorators, target, key, desc) {
2886
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2887
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2888
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2889
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2890
+ };
2891
+ var __metadata16 = function(k, v) {
2892
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2893
+ };
2894
+ var __param6 = function(paramIndex, decorator) {
2895
+ return function(target, key) {
2896
+ decorator(target, key, paramIndex);
2897
+ };
2898
+ };
2899
+ var _a10;
2900
+ var RoleController = class RoleController2 {
2901
+ static {
2902
+ __name(this, "RoleController");
2903
+ }
2904
+ roleService;
2905
+ constructor(roleService) {
2906
+ this.roleService = roleService;
2907
+ }
2908
+ async getRoles() {
2909
+ return this.roleService.getAll();
2910
+ }
2911
+ async getRole(params) {
2912
+ return this.roleService.getById(params.id);
2913
+ }
2914
+ async createRole(body) {
2915
+ return this.roleService.create(body);
2916
+ }
2917
+ async updateRole(params, body) {
2918
+ return this.roleService.update(params.id, body);
2919
+ }
2920
+ async deleteRole(params) {
2921
+ return this.roleService.delete(params.id);
2922
+ }
2923
+ };
2924
+ __decorate16([
2925
+ Get2(),
2926
+ isAdmin(),
2927
+ ResMsg2("roles.success.retrieved"),
2928
+ __metadata16("design:type", Function),
2929
+ __metadata16("design:paramtypes", []),
2930
+ __metadata16("design:returntype", Promise)
2931
+ ], RoleController.prototype, "getRoles", null);
2932
+ __decorate16([
2933
+ Get2("/:id"),
2934
+ isAdmin(),
2935
+ Validate2({ params: roleIdParam }),
2936
+ ResMsg2("roles.success.retrieved"),
2937
+ __param6(0, Params()),
2938
+ __metadata16("design:type", Function),
2939
+ __metadata16("design:paramtypes", [Object]),
2940
+ __metadata16("design:returntype", Promise)
2941
+ ], RoleController.prototype, "getRole", null);
2942
+ __decorate16([
2943
+ Post2(),
2944
+ isAdmin(),
2945
+ Validate2(createRoleDto),
2946
+ ResMsg2("roles.success.created"),
2947
+ __param6(0, Body2()),
2948
+ __metadata16("design:type", Function),
2949
+ __metadata16("design:paramtypes", [Object]),
2950
+ __metadata16("design:returntype", Promise)
2951
+ ], RoleController.prototype, "createRole", null);
2952
+ __decorate16([
2953
+ Put("/:id"),
2954
+ isAdmin(),
2955
+ Validate2({
2956
+ params: roleIdParam,
2957
+ body: updateRoleDto
2958
+ }),
2959
+ ResMsg2("roles.success.updated"),
2960
+ __param6(0, Params()),
2961
+ __param6(1, Body2()),
2962
+ __metadata16("design:type", Function),
2963
+ __metadata16("design:paramtypes", [Object, Object]),
2964
+ __metadata16("design:returntype", Promise)
2965
+ ], RoleController.prototype, "updateRole", null);
2966
+ __decorate16([
2967
+ Delete("/:id"),
2968
+ isAdmin(),
2969
+ Validate2({ params: roleIdParam }),
2970
+ ResMsg2("roles.success.deleted"),
2971
+ __param6(0, Params()),
2972
+ __metadata16("design:type", Function),
2973
+ __metadata16("design:paramtypes", [Object]),
2974
+ __metadata16("design:returntype", Promise)
2975
+ ], RoleController.prototype, "deleteRole", null);
2976
+ RoleController = __decorate16([
2977
+ Controller2("/roles"),
2978
+ __metadata16("design:paramtypes", [typeof (_a10 = typeof RoleService !== "undefined" && RoleService) === "function" ? _a10 : Object])
2979
+ ], RoleController);
2980
+
2981
+ // src/users/UserController.ts
2922
2982
  import { Validate as Validate3 } from "najm-validation";
2923
2983
  var __decorate17 = function(decorators, target, key, desc) {
2924
2984
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -3291,8 +3351,8 @@ import { Injectable as Injectable10 } from "najm-core";
3291
3351
 
3292
3352
  // src/permissions/PermissionValidator.ts
3293
3353
  import { Injectable as Injectable9 } from "najm-core";
3294
- import { I18n as I18n6 } from "najm-i18n";
3295
- import { Err as Err7 } from "najm-core";
3354
+ import { I18n as I18n7 } from "najm-i18n";
3355
+ import { Err as Err8 } from "najm-core";
3296
3356
  var __decorate20 = function(decorators, target, key, desc) {
3297
3357
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3298
3358
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -3321,7 +3381,7 @@ var PermissionValidator = class PermissionValidator2 {
3321
3381
  async checkPermissionExists(id) {
3322
3382
  const permission = await this.permissionRepository.getById(id);
3323
3383
  if (!permission) {
3324
- Err7(this.t("errors.notFound"), 404);
3384
+ Err8(this.t("errors.notFound"), 404);
3325
3385
  }
3326
3386
  return permission;
3327
3387
  }
@@ -3331,7 +3391,7 @@ var PermissionValidator = class PermissionValidator2 {
3331
3391
  async checkPermissionExistsByName(name) {
3332
3392
  const permission = await this.permissionRepository.getByName(name);
3333
3393
  if (!permission) {
3334
- Err7(this.t("errors.notFound"), 404);
3394
+ Err8(this.t("errors.notFound"), 404);
3335
3395
  }
3336
3396
  return permission;
3337
3397
  }
@@ -3343,7 +3403,7 @@ var PermissionValidator = class PermissionValidator2 {
3343
3403
  return;
3344
3404
  const existingPermission = await this.permissionRepository.getByName(name);
3345
3405
  if (existingPermission && existingPermission.id !== excludeId) {
3346
- Err7(this.t("errors.nameExists"), 409);
3406
+ Err8(this.t("errors.nameExists"), 409);
3347
3407
  }
3348
3408
  }
3349
3409
  /**
@@ -3366,12 +3426,12 @@ var PermissionValidator = class PermissionValidator2 {
3366
3426
  await this.checkPermissionExists(permissionId);
3367
3427
  const hasPermission = await this.permissionRepository.checkRoleHasPermission(roleId, permissionId);
3368
3428
  if (hasPermission) {
3369
- Err7(this.t("errors.roleAlreadyHasPermission"), 409);
3429
+ Err8(this.t("errors.roleAlreadyHasPermission"), 409);
3370
3430
  }
3371
3431
  }
3372
3432
  };
3373
3433
  __decorate20([
3374
- I18n6("permissions"),
3434
+ I18n7("permissions"),
3375
3435
  __metadata20("design:type", Object)
3376
3436
  ], PermissionValidator.prototype, "t", void 0);
3377
3437
  PermissionValidator = __decorate20([
@@ -4393,7 +4453,8 @@ var en_default = {
4393
4453
  unauthorized: "Unauthorized access",
4394
4454
  sessionExpired: "Session has expired",
4395
4455
  accountLocked: "Account is temporarily locked. Please try again later.",
4396
- accountInactive: "Account is inactive. Please contact support."
4456
+ accountInactive: "Account is inactive. Please contact support.",
4457
+ emailNotVerified: "Please verify your email address before signing in."
4397
4458
  },
4398
4459
  success: {
4399
4460
  login: "Login successful",
@@ -4436,7 +4497,9 @@ var en_default = {
4436
4497
  notFound: "Role not found",
4437
4498
  exists: "Role already exists",
4438
4499
  nameRequired: "Role name is required",
4439
- cannotDeleteSystem: "Cannot delete system role"
4500
+ cannotDeleteSystem: "Cannot delete system role",
4501
+ cannotRenameSystem: "Cannot rename the system admin role",
4502
+ roleInUse: "Cannot delete a role that is assigned to users"
4440
4503
  },
4441
4504
  success: {
4442
4505
  created: "Role created successfully",
@@ -4500,6 +4563,8 @@ var mergeConfig = /* @__PURE__ */ __name((config) => {
4500
4563
  defaultRole: config?.defaultRole ?? null,
4501
4564
  frontendUrl: config?.frontendUrl ?? process.env.FRONTEND_URL ?? "http://localhost:3000",
4502
4565
  registrationMode: config?.registrationMode ?? "active",
4566
+ requireVerifiedEmail: config?.requireVerifiedEmail ?? false,
4567
+ refreshCookiePath: config?.refreshCookiePath ?? "/",
4503
4568
  lockout: {
4504
4569
  maxAttempts: config?.lockout?.maxAttempts ?? 5,
4505
4570
  duration: config?.lockout?.duration ?? "15m"
@@ -4513,10 +4578,10 @@ var mergeConfig = /* @__PURE__ */ __name((config) => {
4513
4578
  }
4514
4579
  };
4515
4580
  if (!finalConfig.jwt.accessSecret) {
4516
- throw Err8.configRequired("auth", "JWT_ACCESS_SECRET");
4581
+ throw Err9.configRequired("auth", "JWT_ACCESS_SECRET");
4517
4582
  }
4518
4583
  if (!finalConfig.jwt.refreshSecret) {
4519
- throw Err8.configRequired("auth", "JWT_REFRESH_SECRET");
4584
+ throw Err9.configRequired("auth", "JWT_REFRESH_SECRET");
4520
4585
  }
4521
4586
  return finalConfig;
4522
4587
  }, "mergeConfig");
@@ -4752,6 +4817,7 @@ export {
4752
4817
  permissionsTable,
4753
4818
  pickProps,
4754
4819
  refreshTokenDto,
4820
+ registerDto,
4755
4821
  resetPasswordDto,
4756
4822
  revokeTokenDto,
4757
4823
  roleIdParam,