najm-auth 1.1.17 → 1.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -198,11 +198,13 @@ class PostController {
198
198
  ```typescript
199
199
  import { defineRoles } from 'najm-auth';
200
200
 
201
- const roles = defineRoles({
202
- ADMIN: 'admin',
203
- MODERATOR: 'moderator',
204
- USER: 'user',
205
- });
201
+ const roles = defineRoles({
202
+ ADMIN: 'admin',
203
+ MODERATOR: 'moderator',
204
+ USER: 'user',
205
+ }, {
206
+ superRoles: ['ADMIN'], // admin also passes moderator/user role guards
207
+ });
206
208
 
207
209
  export const { isAdmin, isModerator, isUser } = roles;
208
210
 
package/dist/index.d.ts CHANGED
@@ -683,8 +683,10 @@ declare class TokenService {
683
683
  validateRefreshSession(refreshToken: string): Promise<string>;
684
684
  /**
685
685
  * Read the refresh cookie and return the userId it belongs to.
686
- * Validates against DB to ensure the session is still active.
687
- * Throws if the cookie is missing, invalid, or revoked.
686
+ * Lightweight check: verifies JWT signature and ensures the user
687
+ * has an active session in the DB. Does NOT compare token hashes,
688
+ * so it is safe to call concurrently with token rotation.
689
+ * Throws if the cookie is missing, invalid, or the session was revoked.
688
690
  */
689
691
  resolveUserFromCookie(): Promise<string>;
690
692
  getUser(auth: string): Promise<any>;
@@ -860,6 +862,13 @@ declare class AuthService {
860
862
  getUserFromCookie(): Promise<SanitizedUser & {
861
863
  language: string;
862
864
  }>;
865
+ /**
866
+ * Get current user — prefer access token (no cookie rotation risk),
867
+ * fall back to cookie when no Authorization header is present.
868
+ */
869
+ getMe(authorization?: string): Promise<SanitizedUser & {
870
+ language: string;
871
+ }>;
863
872
  forgotPassword(email: string): Promise<{
864
873
  message: string;
865
874
  }>;
@@ -887,7 +896,7 @@ declare class AuthController {
887
896
  changePassword(userId: string, body: ChangePasswordDto): Promise<{
888
897
  message: string;
889
898
  }>;
890
- userProfile(): Promise<Omit<{
899
+ userProfile(authorization?: string): Promise<Omit<{
891
900
  id: string;
892
901
  name: string;
893
902
  createdAt: string;
@@ -982,6 +991,13 @@ type RoleInput = string | string[];
982
991
  type IsGuards<T extends Record<string, string>> = {
983
992
  [K in keyof T as `is${Capitalize<Lowercase<string & K>>}`]: () => ClassDecorator & MethodDecorator;
984
993
  };
994
+ interface DefineRolesOptions<T extends Record<string, string>> {
995
+ /**
996
+ * Role keys that should implicitly pass every generated role guard, group guard,
997
+ * and service-layer role check created by defineRoles.
998
+ */
999
+ superRoles?: Array<keyof T>;
1000
+ }
985
1001
  interface DefineRolesResult<T extends Record<string, string>> {
986
1002
  /** The original roles record */
987
1003
  ROLES: T;
@@ -1001,6 +1017,8 @@ interface DefineRolesResult<T extends Record<string, string>> {
1001
1017
  * ADMIN: 'admin',
1002
1018
  * TEACHER: 'teacher',
1003
1019
  * STUDENT: 'student',
1020
+ * }, {
1021
+ * superRoles: ['ADMIN'],
1004
1022
  * });
1005
1023
  *
1006
1024
  * // HTTP guards
@@ -1010,7 +1028,7 @@ interface DefineRolesResult<T extends Record<string, string>> {
1010
1028
  * if (hasRole(user.role, 'ADMIN', 'TEACHER')) { ... }
1011
1029
  * ```
1012
1030
  */
1013
- declare function defineRoles<T extends Record<string, string>>(roles: T): DefineRolesResult<T> & IsGuards<T>;
1031
+ declare function defineRoles<T extends Record<string, string>>(roles: T, options?: DefineRolesOptions<T>): DefineRolesResult<T> & IsGuards<T>;
1014
1032
 
1015
1033
  declare class RoleGuard {
1016
1034
  canActivate(allowedRoles: RoleInput, userRole: string): false | {
@@ -1818,4 +1836,4 @@ declare const authSeed: (config: AuthSeedConfig) => Record<string, SeedEntry>;
1818
1836
  */
1819
1837
  declare function seedAuthData(config: SeedAuthDataConfig): Promise<SeedAuthDataResult>;
1820
1838
 
1821
- export { AUTH_CONFIG, en as AUTH_EN, AUTH_LOCALES, AUTH_MODULE, AUTH_PERMISSIONS, AUTH_ROLE, AUTH_SCHEMA, AUTH_SUPPORTED_LANGUAGES, AUTH_USER, type AssignPermissionDto, type AssignRoleDto, type AssignRoleParams, type AuthConfig, AuthController, AuthGuard, type AuthPluginConfig, AuthQueries, AuthResolver, type AuthSchema, type AuthSeedConfig, AuthService, type AuthUser, 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 EmailParam, EncryptionService, type JwtConfig, type JwtPayload, type LanguageParam, type LoginDto, NewPermission, NewRoleEntity, NewUser, Owned, type OwnedMethods, type OwnershipConfig, type OwnershipProvider, type OwnershipRule, OwnershipToken, type OwnershipTokenOptions, Permission, PermissionController, PermissionGuard, type PermissionIdParam, PermissionRepository, PermissionService, PermissionValidator, Policy, ROLES, ROLE_GROUPS, type RefreshTokenDto, type ResetPasswordDto, type ResourceAccessor, type ResourceGuards, type ResourceGuardsOptions, type RevokeTokenDto, Role, RoleController, RoleEntity, RoleGuard, type RoleIdParam, type RoleInput, RolePermission, RoleRepository, RoleService, type RoleType, RoleValidator, type SanitizedUser, ScopeContext, type ScopeResult, type SeedAuthDataConfig, type SeedAuthDataResult, type SeedUserConfig, TOKEN_STATUS, TOKEN_TYPE, type TokenIdParam, type TokenPair, TokenRepository, TokenService, USER_STATUS, type UpdatePermissionDto, type UpdateRoleDto, type UpdateTokenDto, type UpdateUserDto, User, UserController, type UserIdInParam, type UserIdParam, UserRepository, UserService, UserValidator, type UserWithPermissions, type VerifyTokenDto, assignPermissionDto, assignRoleDto, assignRoleParams, auth$1 as auth, authSeed, avatarsPath, calculateAge, calculateYearsOfExperience, changePasswordDto, checkPermissionDto, clean, configureOwnership, confirmResetPasswordDto, createPermissionDto, createRoleDto, createTokenDto, createUserDto, defineRoles, emailParam, formatDate, getAuthLocale, getAvatarFile, isAdmin, isAdministrator, isAuth, isEmpty, isFile, isPath, join, languageParam, loginDto, own, parseSchema, permissionIdParam, pickProps, refreshTokenDto, resetPasswordDto, revokeTokenDto, roleIdParam, seedAuthData, setConfiguredCookieName, tokenIdParam, updatePermissionDto, updateRoleDto, updateTokenDto, updateUserDto, userIdInParam, userIdParam, verifyTokenDto, where };
1839
+ export { AUTH_CONFIG, en as AUTH_EN, AUTH_LOCALES, AUTH_MODULE, AUTH_PERMISSIONS, AUTH_ROLE, AUTH_SCHEMA, AUTH_SUPPORTED_LANGUAGES, AUTH_USER, type AssignPermissionDto, type AssignRoleDto, type AssignRoleParams, type AuthConfig, AuthController, AuthGuard, type AuthPluginConfig, AuthQueries, AuthResolver, type AuthSchema, type AuthSeedConfig, AuthService, type AuthUser, 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 DefineRolesOptions, type EmailParam, EncryptionService, type JwtConfig, type JwtPayload, type LanguageParam, type LoginDto, NewPermission, NewRoleEntity, NewUser, Owned, type OwnedMethods, type OwnershipConfig, type OwnershipProvider, type OwnershipRule, OwnershipToken, type OwnershipTokenOptions, Permission, PermissionController, PermissionGuard, type PermissionIdParam, PermissionRepository, PermissionService, PermissionValidator, Policy, ROLES, ROLE_GROUPS, type RefreshTokenDto, type ResetPasswordDto, type ResourceAccessor, type ResourceGuards, type ResourceGuardsOptions, type RevokeTokenDto, Role, RoleController, RoleEntity, RoleGuard, type RoleIdParam, type RoleInput, RolePermission, RoleRepository, RoleService, type RoleType, RoleValidator, type SanitizedUser, ScopeContext, type ScopeResult, type SeedAuthDataConfig, type SeedAuthDataResult, type SeedUserConfig, TOKEN_STATUS, TOKEN_TYPE, type TokenIdParam, type TokenPair, TokenRepository, TokenService, USER_STATUS, type UpdatePermissionDto, type UpdateRoleDto, type UpdateTokenDto, type UpdateUserDto, User, UserController, type UserIdInParam, type UserIdParam, UserRepository, UserService, UserValidator, type UserWithPermissions, type VerifyTokenDto, assignPermissionDto, assignRoleDto, assignRoleParams, auth$1 as auth, authSeed, avatarsPath, calculateAge, calculateYearsOfExperience, changePasswordDto, checkPermissionDto, clean, configureOwnership, confirmResetPasswordDto, createPermissionDto, createRoleDto, createTokenDto, createUserDto, defineRoles, emailParam, formatDate, getAuthLocale, getAvatarFile, isAdmin, isAdministrator, isAuth, isEmpty, isFile, isPath, join, languageParam, loginDto, own, parseSchema, permissionIdParam, pickProps, refreshTokenDto, resetPasswordDto, revokeTokenDto, roleIdParam, seedAuthData, setConfiguredCookieName, tokenIdParam, updatePermissionDto, updateRoleDto, updateTokenDto, updateUserDto, userIdInParam, userIdParam, verifyTokenDto, where };
package/dist/index.js CHANGED
@@ -1408,24 +1408,28 @@ var TokenService = class TokenService2 {
1408
1408
  }
1409
1409
  const isValid = this.hashToken(refreshToken) === stored.token;
1410
1410
  if (!isValid) {
1411
- if (stored.tokenFamily) {
1412
- await this.tokenRepository.revokeByFamily(stored.tokenFamily);
1413
- }
1414
1411
  Err5(this.t("errors.refreshTokenInvalid"));
1415
1412
  }
1416
1413
  return userId;
1417
1414
  }
1418
1415
  /**
1419
1416
  * Read the refresh cookie and return the userId it belongs to.
1420
- * Validates against DB to ensure the session is still active.
1421
- * Throws if the cookie is missing, invalid, or revoked.
1417
+ * Lightweight check: verifies JWT signature and ensures the user
1418
+ * has an active session in the DB. Does NOT compare token hashes,
1419
+ * so it is safe to call concurrently with token rotation.
1420
+ * Throws if the cookie is missing, invalid, or the session was revoked.
1422
1421
  */
1423
1422
  async resolveUserFromCookie() {
1424
1423
  const refreshToken = this.cookieManager.getRefreshToken();
1425
1424
  if (!refreshToken) {
1426
1425
  Err5(this.t("errors.refreshTokenMissing"));
1427
1426
  }
1428
- return this.validateRefreshSession(refreshToken);
1427
+ const userId = this.verifyRefreshToken(refreshToken);
1428
+ const stored = await this.tokenRepository.getRefreshTokenWithFamily(userId);
1429
+ if (!stored) {
1430
+ Err5(this.t("errors.refreshTokenInvalid"));
1431
+ }
1432
+ return userId;
1429
1433
  }
1430
1434
  // ============ USER RETRIEVAL (MAIN METHOD) ============
1431
1435
  async getUser(auth2) {
@@ -1552,9 +1556,6 @@ var TokenService = class TokenService2 {
1552
1556
  }
1553
1557
  const isValid = this.hashToken(refreshToken) === stored.token;
1554
1558
  if (!isValid) {
1555
- if (stored.tokenFamily) {
1556
- await this.tokenRepository.revokeByFamily(stored.tokenFamily);
1557
- }
1558
1559
  Err5(this.t("errors.refreshTokenInvalid"));
1559
1560
  }
1560
1561
  return this.generateTokens(userId, stored.tokenFamily ?? void 0);
@@ -1758,6 +1759,20 @@ var AuthService = class AuthService2 {
1758
1759
  const lang = this.i18nService.getCurrentLanguage();
1759
1760
  return { ...user, language: lang };
1760
1761
  }
1762
+ /**
1763
+ * Get current user — prefer access token (no cookie rotation risk),
1764
+ * fall back to cookie when no Authorization header is present.
1765
+ */
1766
+ async getMe(authorization) {
1767
+ if (authorization) {
1768
+ const user = await this.tokenService.getUser(authorization);
1769
+ if (user) {
1770
+ const lang = this.i18nService.getCurrentLanguage();
1771
+ return { ...user, language: lang };
1772
+ }
1773
+ }
1774
+ return this.getUserFromCookie();
1775
+ }
1761
1776
  async forgotPassword(email2) {
1762
1777
  const user = await this.userService.findByEmail(email2);
1763
1778
  if (user) {
@@ -1966,8 +1981,8 @@ var AuthController = class AuthController2 {
1966
1981
  async changePassword(userId, body) {
1967
1982
  return this.authService.changePassword(userId, body.currentPassword, body.newPassword);
1968
1983
  }
1969
- async userProfile() {
1970
- return this.authService.getUserFromCookie();
1984
+ async userProfile(authorization) {
1985
+ return this.authService.getMe(authorization);
1971
1986
  }
1972
1987
  async forgotPassword(body) {
1973
1988
  return this.authService.forgotPassword(body.email);
@@ -2029,8 +2044,9 @@ __decorate13([
2029
2044
  Get("/me"),
2030
2045
  RateLimit({ limit: 30, window: "1m", key: cookieFingerprint() }),
2031
2046
  ResMsg("auth.users.success.retrieved"),
2047
+ __param3(0, Headers("authorization")),
2032
2048
  __metadata13("design:type", Function),
2033
- __metadata13("design:paramtypes", []),
2049
+ __metadata13("design:paramtypes", [String]),
2034
2050
  __metadata13("design:returntype", Promise)
2035
2051
  ], AuthController.prototype, "userProfile", null);
2036
2052
  __decorate13([
@@ -2299,15 +2315,21 @@ var isAdministrator = composeGuards(isAuth(), Role(ROLE_GROUPS.ADMINISTRATORS));
2299
2315
 
2300
2316
  // src/roles/defineRoles.ts
2301
2317
  var Role2 = createGuard3(RoleGuard);
2302
- function defineRoles(roles) {
2318
+ function defineRoles(roles, options) {
2303
2319
  const ROLES2 = roles;
2320
+ const superRoleKeys = options?.superRoles ?? [];
2321
+ function resolveRoleValues(keys) {
2322
+ return Array.from(new Set([...keys, ...superRoleKeys].map((key) => roles[key])));
2323
+ }
2324
+ __name(resolveRoleValues, "resolveRoleValues");
2304
2325
  const guards2 = {};
2305
2326
  for (const [key, value] of Object.entries(roles)) {
2306
2327
  const name = `is${key.charAt(0).toUpperCase()}${key.slice(1).toLowerCase()}`;
2307
- guards2[name] = composeGuards2(isAuth(), Role2(value));
2328
+ const allowedValues = resolveRoleValues([key]);
2329
+ guards2[name] = composeGuards2(isAuth(), Role2(allowedValues.length === 1 ? value : allowedValues));
2308
2330
  }
2309
2331
  function createGroupGuard(keys) {
2310
- const values = keys.map((k) => roles[k]);
2332
+ const values = resolveRoleValues(keys);
2311
2333
  return composeGuards2(isAuth(), Role2(values));
2312
2334
  }
2313
2335
  __name(createGroupGuard, "createGroupGuard");
@@ -2315,7 +2337,7 @@ function defineRoles(roles) {
2315
2337
  if (!userRole)
2316
2338
  return false;
2317
2339
  const normalized = userRole.toLowerCase();
2318
- return keys.some((k) => roles[k] === normalized);
2340
+ return resolveRoleValues(keys).some((role) => role === normalized);
2319
2341
  }
2320
2342
  __name(hasRole, "hasRole");
2321
2343
  function isInGroup(userRole, keys) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-auth",
3
- "version": "1.1.17",
3
+ "version": "1.1.20",
4
4
  "description": "Authentication and authorization library for najm framework",
5
5
  "type": "module",
6
6
  "files": [