najm-auth 3.1.2 → 3.1.4

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
@@ -721,7 +721,7 @@ limits are active when `auth()` is registered.
721
721
  | Route | Limit | Window | Key Strategy |
722
722
  |-------|-------|--------|--------------|
723
723
  | `POST /auth/register` | 5 | 15 minutes | IP |
724
- | `POST /auth/login` | 5 | 15 minutes | IP |
724
+ | `POST /auth/login` | 8 | 10 minutes | IP + hashed normalized identity |
725
725
  | `POST /auth/refresh` | 15 | 15 minutes | Cookie fingerprint |
726
726
  | `POST /auth/session/recover` | 120 | 1 minute | Cookie fingerprint |
727
727
  | `POST /auth/logout` | 10 | 15 minutes | User ID |
@@ -729,10 +729,27 @@ limits are active when `auth()` is registered.
729
729
  | `POST /auth/forgot-password` | 3 | 15 minutes | IP |
730
730
  | `POST /auth/reset-password` | 5 | 15 minutes | IP |
731
731
 
732
- ### Customizing Rate Limits
733
-
734
- ```typescript
735
- auth({
732
+ ### Customizing Rate Limits
733
+
734
+ The login route has strict environment overrides. Values are read when the
735
+ server imports `najm-auth`, so restart the process after changing them. Invalid
736
+ values fail startup rather than silently weakening the limiter.
737
+
738
+ ```bash
739
+ NAJM_AUTH_LOGIN_RATE_LIMIT_ENABLED=true
740
+ NAJM_AUTH_LOGIN_RATE_LIMIT=8
741
+ NAJM_AUTH_LOGIN_RATE_WINDOW=10m
742
+ ```
743
+
744
+ `NAJM_AUTH_LOGIN_RATE_LIMIT_ENABLED=false` disables only the login-route
745
+ limiter. Keep it enabled on public production deployments; a shorter window is
746
+ the safer setting for a disposable production-built demo.
747
+
748
+ The generic plugin configuration remains available for global limits and skip
749
+ rules:
750
+
751
+ ```typescript
752
+ auth({
736
753
  rateLimit: {
737
754
  keyGenerator: 'ip', // or 'user', 'api-key', 'user+ip'
738
755
  defaultWindow: '10m',
@@ -463,7 +463,8 @@ var NajmAuthClient = class _NajmAuthClient {
463
463
  this._hydrated = true;
464
464
  this.resetRefreshFailures();
465
465
  if (!session || !session.user) {
466
- this.api.blockAuthenticatedRequests();
466
+ if (this.refreshBlocked) this.api.blockAuthenticatedRequests();
467
+ else this.api.allowAuthenticatedRequests();
467
468
  this.state = { ...INITIAL_STATE };
468
469
  this.notify();
469
470
  return;
@@ -1172,7 +1172,8 @@ var NajmAuthClient = class _NajmAuthClient {
1172
1172
  this._hydrated = true;
1173
1173
  this.resetRefreshFailures();
1174
1174
  if (!session || !session.user) {
1175
- this.api.blockAuthenticatedRequests();
1175
+ if (this.refreshBlocked) this.api.blockAuthenticatedRequests();
1176
+ else this.api.allowAuthenticatedRequests();
1176
1177
  this.state = { ...INITIAL_STATE };
1177
1178
  this.notify();
1178
1179
  return;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as najm_core from 'najm-core';
2
2
  import { Container } from 'najm-core';
3
3
  import { ValidationPluginConfig } from 'najm-validation';
4
- import { RateLimitPluginConfig } from 'najm-rate';
4
+ import { RateLimitPluginConfig, TimeWindow } from 'najm-rate';
5
5
  import { EmailPluginConfig, EmailService } from 'najm-email';
6
6
  import { R as ResolvedIdentityConfig, I as IdentityConfig, T as TemporaryCredentialInput } from './ma-sNHnUGLO.js';
7
7
  export { D as DEFAULT_IDENTITY_PRESET, E as EXACT_TEMPORARY_CREDENTIAL_KIND, a as IDENTITY_PRESETS, b as IdentityNormalizer, c as IdentityPreset, d as IdentityPresetName, M as MOROCCAN_CIN_TEMPORARY_CREDENTIAL_KIND, e as TemporaryCredential, f as TemporaryCredentialKind, g as compactPhone, i as isMoroccanCin, h as isTemporaryCredentialKind, m as moroccanCinTemporaryCredential, j as moroccoIdentityPreset, n as normalizeMoroccanCin, k as normalizeMoroccanPhone, l as normalizeTunisianPhone, r as resolveTemporaryCredentialKind, t as toTemporaryCredential, o as tunisiaIdentityPreset } from './ma-sNHnUGLO.js';
@@ -1786,6 +1786,24 @@ declare function createIdentityResolver(config?: IdentityConfig): IdentityResolv
1786
1786
  declare function normalizeAuthIdentifier(value: unknown): string | null;
1787
1787
  declare function isEmailIdentifier(value: string): boolean;
1788
1788
 
1789
+ declare const AUTH_LOGIN_RATE_LIMIT_ENV: {
1790
+ readonly enabled: "NAJM_AUTH_LOGIN_RATE_LIMIT_ENABLED";
1791
+ readonly limit: "NAJM_AUTH_LOGIN_RATE_LIMIT";
1792
+ readonly window: "NAJM_AUTH_LOGIN_RATE_WINDOW";
1793
+ };
1794
+ interface AuthLoginRateLimitConfig {
1795
+ enabled: boolean;
1796
+ limit: number;
1797
+ window: TimeWindow;
1798
+ }
1799
+ type AuthRateLimitEnvironment = Record<string, string | undefined>;
1800
+ declare const DEFAULT_AUTH_LOGIN_RATE_LIMIT: {
1801
+ readonly enabled: true;
1802
+ readonly limit: 8;
1803
+ readonly window: "10m";
1804
+ };
1805
+ declare function resolveAuthLoginRateLimitConfig(env?: AuthRateLimitEnvironment): AuthLoginRateLimitConfig;
1806
+
1789
1807
  interface RunAsUser {
1790
1808
  id: string;
1791
1809
  role?: string | null;
@@ -2747,4 +2765,4 @@ declare const authSeed: (config: AuthSeedConfig) => Record<string, SeedEntry>;
2747
2765
  */
2748
2766
  declare function seedAuthData(config: SeedAuthDataConfig): Promise<SeedAuthDataResult>;
2749
2767
 
2750
- 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, 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_CREDENTIAL_SETUP_COOKIE_NAME, DEFAULT_CREDENTIAL_SETUP_TTL_MS, type DefineRolesOptions, type EmailParam, EncryptionService, 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, PasswordSetupService, Permission, PermissionController, PermissionGuard, type PermissionIdParam, PermissionRepository, PermissionService, PermissionValidator, Policy, type ProvisionUserInput, type ProvisionUserWithPasswordInput, type ProvisionUserWithSetupInput, ROLES, ROLE_GROUPS, type RefreshTokenDto, type RegisterDto, 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, revokeTokenDto, roleIdParam, runAsUser, seedAuthData, tokenIdParam, updatePermissionDto, updateRoleDto, updateTokenDto, updateUserDto, userIdInParam, userIdParam, userListQuery, verifyTokenDto, where };
2768
+ export { AUTH_CONFIG, 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 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, PasswordSetupService, Permission, PermissionController, PermissionGuard, type PermissionIdParam, PermissionRepository, PermissionService, PermissionValidator, Policy, type ProvisionUserInput, type ProvisionUserWithPasswordInput, type ProvisionUserWithSetupInput, ROLES, ROLE_GROUPS, type RefreshTokenDto, type RegisterDto, 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 };
package/dist/index.js CHANGED
@@ -3583,6 +3583,75 @@ function getRequestIdentityResolver(context) {
3583
3583
  }
3584
3584
  __name(getRequestIdentityResolver, "getRequestIdentityResolver");
3585
3585
 
3586
+ // src/auth/authLoginRateLimitConfig.ts
3587
+ var AUTH_LOGIN_RATE_LIMIT_ENV = {
3588
+ enabled: "NAJM_AUTH_LOGIN_RATE_LIMIT_ENABLED",
3589
+ limit: "NAJM_AUTH_LOGIN_RATE_LIMIT",
3590
+ window: "NAJM_AUTH_LOGIN_RATE_WINDOW"
3591
+ };
3592
+ var DEFAULT_AUTH_LOGIN_RATE_LIMIT = {
3593
+ enabled: true,
3594
+ limit: 8,
3595
+ window: "10m"
3596
+ };
3597
+ function parseEnabled(raw) {
3598
+ if (raw === void 0 || raw.trim() === "") {
3599
+ return DEFAULT_AUTH_LOGIN_RATE_LIMIT.enabled;
3600
+ }
3601
+ const normalized = raw.trim().toLowerCase();
3602
+ if (normalized === "true")
3603
+ return true;
3604
+ if (normalized === "false")
3605
+ return false;
3606
+ throw new Error(`${AUTH_LOGIN_RATE_LIMIT_ENV.enabled} must be true or false`);
3607
+ }
3608
+ __name(parseEnabled, "parseEnabled");
3609
+ function parseLimit(raw) {
3610
+ if (raw === void 0 || raw.trim() === "") {
3611
+ return DEFAULT_AUTH_LOGIN_RATE_LIMIT.limit;
3612
+ }
3613
+ const normalized = raw.trim();
3614
+ if (!/^\d+$/.test(normalized)) {
3615
+ throw new Error(`${AUTH_LOGIN_RATE_LIMIT_ENV.limit} must be a positive safe integer`);
3616
+ }
3617
+ const limit = Number(normalized);
3618
+ if (!Number.isSafeInteger(limit) || limit <= 0) {
3619
+ throw new Error(`${AUTH_LOGIN_RATE_LIMIT_ENV.limit} must be a positive safe integer`);
3620
+ }
3621
+ return limit;
3622
+ }
3623
+ __name(parseLimit, "parseLimit");
3624
+ function parseWindow(raw) {
3625
+ if (raw === void 0 || raw.trim() === "") {
3626
+ return DEFAULT_AUTH_LOGIN_RATE_LIMIT.window;
3627
+ }
3628
+ const normalized = raw.trim().toLowerCase();
3629
+ const match = normalized.match(/^([1-9]\d*)(s|m|h|d)$/);
3630
+ if (!match) {
3631
+ throw new Error(`${AUTH_LOGIN_RATE_LIMIT_ENV.window} must be a positive duration such as 30s, 10m, 1h, or 1d`);
3632
+ }
3633
+ const amount = Number(match[1]);
3634
+ const multiplier = {
3635
+ s: 1e3,
3636
+ m: 6e4,
3637
+ h: 36e5,
3638
+ d: 864e5
3639
+ }[match[2]];
3640
+ if (!Number.isSafeInteger(amount) || !Number.isSafeInteger(amount * multiplier)) {
3641
+ throw new Error(`${AUTH_LOGIN_RATE_LIMIT_ENV.window} is too large`);
3642
+ }
3643
+ return normalized;
3644
+ }
3645
+ __name(parseWindow, "parseWindow");
3646
+ function resolveAuthLoginRateLimitConfig(env = process.env) {
3647
+ return {
3648
+ enabled: parseEnabled(env[AUTH_LOGIN_RATE_LIMIT_ENV.enabled]),
3649
+ limit: parseLimit(env[AUTH_LOGIN_RATE_LIMIT_ENV.limit]),
3650
+ window: parseWindow(env[AUTH_LOGIN_RATE_LIMIT_ENV.window])
3651
+ };
3652
+ }
3653
+ __name(resolveAuthLoginRateLimitConfig, "resolveAuthLoginRateLimitConfig");
3654
+
3586
3655
  // src/users/UserDto.ts
3587
3656
  import { z } from "zod";
3588
3657
  var emailField = z.string().email("Invalid email format");
@@ -3692,6 +3761,7 @@ var authIdentityRateLimitKey = /* @__PURE__ */ __name(async (ctx) => {
3692
3761
  }
3693
3762
  return ip;
3694
3763
  }, "authIdentityRateLimitKey");
3764
+ var loginRateLimit = resolveAuthLoginRateLimitConfig();
3695
3765
  var AuthController = class AuthController2 {
3696
3766
  static {
3697
3767
  __name(this, "AuthController");
@@ -3748,7 +3818,13 @@ __decorate20([
3748
3818
  ], AuthController.prototype, "registerUser", null);
3749
3819
  __decorate20([
3750
3820
  Post("/login"),
3751
- RateLimit({ limit: 5, window: "15m", key: authIdentityRateLimitKey, message: "Too many login attempts. Please try again later." }),
3821
+ RateLimit({
3822
+ limit: loginRateLimit.limit,
3823
+ window: loginRateLimit.window,
3824
+ key: authIdentityRateLimitKey,
3825
+ message: "Too many login attempts. Please try again later.",
3826
+ skip: !loginRateLimit.enabled
3827
+ }),
3752
3828
  Validate(loginDto),
3753
3829
  ResMsg("auth.success.login"),
3754
3830
  __param5(0, Body()),
@@ -6885,6 +6961,7 @@ export {
6885
6961
  AUTH_CONFIG,
6886
6962
  en_default as AUTH_EN,
6887
6963
  AUTH_LOCALES,
6964
+ AUTH_LOGIN_RATE_LIMIT_ENV,
6888
6965
  AUTH_MODULE,
6889
6966
  AUTH_PERMISSIONS,
6890
6967
  AUTH_ROLE,
@@ -6911,6 +6988,7 @@ export {
6911
6988
  CredentialSetupRequirementRepository,
6912
6989
  CredentialSetupRequirementService,
6913
6990
  CredentialSetupService,
6991
+ DEFAULT_AUTH_LOGIN_RATE_LIMIT,
6914
6992
  DEFAULT_CREDENTIAL_SETUP_COOKIE_NAME,
6915
6993
  DEFAULT_CREDENTIAL_SETUP_TTL_MS,
6916
6994
  DEFAULT_IDENTITY_PRESET,
@@ -7007,6 +7085,7 @@ export {
7007
7085
  refreshTokenDto,
7008
7086
  registerDto,
7009
7087
  resetPasswordDto,
7088
+ resolveAuthLoginRateLimitConfig,
7010
7089
  resolveTemporaryCredentialKind,
7011
7090
  revokeTokenDto,
7012
7091
  roleIdParam,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-auth",
3
- "version": "3.1.2",
3
+ "version": "3.1.4",
4
4
  "description": "Authentication and authorization library for najm framework",
5
5
  "type": "module",
6
6
  "files": [
@@ -97,7 +97,7 @@
97
97
  "najm-i18n": "^2.0.3",
98
98
  "najm-cache": "^2.0.2",
99
99
  "najm-email": "^2.0.2",
100
- "najm-rate": "^2.0.2",
100
+ "najm-rate": "^2.0.3",
101
101
  "najm-validation": "^2.0.2",
102
102
  "jsonwebtoken": "^9.0.3",
103
103
  "jose": "^6.1.3",