najm-auth 3.1.3 → 3.1.5
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 +25 -4
- package/dist/client/server/index.d.ts +2 -2
- package/dist/client/server/index.js +27 -1
- package/dist/index.d.ts +30 -12
- package/dist/index.js +80 -1
- package/dist/schema/pg.d.ts +2 -2
- package/dist/schema/sqlite.d.ts +4 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -272,9 +272,13 @@ login, `AuthSessionService.establish()`, Google OAuth (which redirects with
|
|
|
272
272
|
recovery — so verified-email OAuth linking cannot skip it. Marking a new
|
|
273
273
|
requirement also revokes the user's current sessions.
|
|
274
274
|
|
|
275
|
-
`withAuthCookiePersistence` recognizes
|
|
276
|
-
|
|
277
|
-
|
|
275
|
+
`withAuthCookiePersistence` recognizes logout and setup boundaries on its own.
|
|
276
|
+
After a successful logout it drops stale auth-cookie issuances and guarantees
|
|
277
|
+
exactly one deletion for each configured auth cookie. It preserves a valid
|
|
278
|
+
upstream deletion (including a custom cookie path), or synthesizes a canonical
|
|
279
|
+
deletion when one is missing. A setup response gets the same auth-cookie
|
|
280
|
+
deletions, clears the remembered preference, and leaves the opaque setup cookie
|
|
281
|
+
alone.
|
|
278
282
|
|
|
279
283
|
### Google Sign-In
|
|
280
284
|
|
|
@@ -721,7 +725,7 @@ limits are active when `auth()` is registered.
|
|
|
721
725
|
| Route | Limit | Window | Key Strategy |
|
|
722
726
|
|-------|-------|--------|--------------|
|
|
723
727
|
| `POST /auth/register` | 5 | 15 minutes | IP |
|
|
724
|
-
| `POST /auth/login` |
|
|
728
|
+
| `POST /auth/login` | 8 | 10 minutes | IP + hashed normalized identity |
|
|
725
729
|
| `POST /auth/refresh` | 15 | 15 minutes | Cookie fingerprint |
|
|
726
730
|
| `POST /auth/session/recover` | 120 | 1 minute | Cookie fingerprint |
|
|
727
731
|
| `POST /auth/logout` | 10 | 15 minutes | User ID |
|
|
@@ -731,6 +735,23 @@ limits are active when `auth()` is registered.
|
|
|
731
735
|
|
|
732
736
|
### Customizing Rate Limits
|
|
733
737
|
|
|
738
|
+
The login route has strict environment overrides. Values are read when the
|
|
739
|
+
server imports `najm-auth`, so restart the process after changing them. Invalid
|
|
740
|
+
values fail startup rather than silently weakening the limiter.
|
|
741
|
+
|
|
742
|
+
```bash
|
|
743
|
+
NAJM_AUTH_LOGIN_RATE_LIMIT_ENABLED=true
|
|
744
|
+
NAJM_AUTH_LOGIN_RATE_LIMIT=8
|
|
745
|
+
NAJM_AUTH_LOGIN_RATE_WINDOW=10m
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
`NAJM_AUTH_LOGIN_RATE_LIMIT_ENABLED=false` disables only the login-route
|
|
749
|
+
limiter. Keep it enabled on public production deployments; a shorter window is
|
|
750
|
+
the safer setting for a disposable production-built demo.
|
|
751
|
+
|
|
752
|
+
The generic plugin configuration remains available for global limits and skip
|
|
753
|
+
rules:
|
|
754
|
+
|
|
734
755
|
```typescript
|
|
735
756
|
auth({
|
|
736
757
|
rateLimit: {
|
|
@@ -248,8 +248,8 @@ interface AuthCookiePersistenceOptions {
|
|
|
248
248
|
* Najm's own setup response is recognized without this — supply it only to
|
|
249
249
|
* cover an application-specific shape. Such a response may carry auth
|
|
250
250
|
* cookies anyway, and persisting them would leave a half-authenticated
|
|
251
|
-
* browser that skips the setup step on reload. Returning `true`
|
|
252
|
-
* and clears the stored choice.
|
|
251
|
+
* browser that skips the setup step on reload. Returning `true` replaces
|
|
252
|
+
* them with deletions and clears the stored choice.
|
|
253
253
|
*/
|
|
254
254
|
isSetupResponse?: (payload: unknown) => boolean;
|
|
255
255
|
}
|
|
@@ -1661,6 +1661,18 @@ function clearedRememberCookie(name, secure) {
|
|
|
1661
1661
|
].join("; ");
|
|
1662
1662
|
}
|
|
1663
1663
|
__name(clearedRememberCookie, "clearedRememberCookie");
|
|
1664
|
+
function clearedAuthCookie(name, secure) {
|
|
1665
|
+
return [
|
|
1666
|
+
`${name}=`,
|
|
1667
|
+
"Path=/",
|
|
1668
|
+
"HttpOnly",
|
|
1669
|
+
"SameSite=Lax",
|
|
1670
|
+
...secure ? ["Secure"] : [],
|
|
1671
|
+
"Expires=Thu, 01 Jan 1970 00:00:00 GMT",
|
|
1672
|
+
"Max-Age=0"
|
|
1673
|
+
].join("; ");
|
|
1674
|
+
}
|
|
1675
|
+
__name(clearedAuthCookie, "clearedAuthCookie");
|
|
1664
1676
|
function withAuthCookiePersistence(handler, options = {}) {
|
|
1665
1677
|
const {
|
|
1666
1678
|
authCookieNames = DEFAULTS.authCookieNames,
|
|
@@ -1697,8 +1709,15 @@ function withAuthCookiePersistence(handler, options = {}) {
|
|
|
1697
1709
|
const headers = new Headers(response.headers);
|
|
1698
1710
|
const setCookies = headers.getSetCookie();
|
|
1699
1711
|
headers.delete("set-cookie");
|
|
1712
|
+
const clearedAuthCookies = /* @__PURE__ */ new Set();
|
|
1700
1713
|
for (const setCookie of setCookies) {
|
|
1701
|
-
|
|
1714
|
+
const name = cookieName(setCookie);
|
|
1715
|
+
const isAuthCookie = authCookieNames.includes(name);
|
|
1716
|
+
if ((action.type === "clear" || action.type === "setup") && isAuthCookie) {
|
|
1717
|
+
if (isDeletionCookie(setCookie) && !clearedAuthCookies.has(name)) {
|
|
1718
|
+
headers.append("set-cookie", setCookie);
|
|
1719
|
+
clearedAuthCookies.add(name);
|
|
1720
|
+
}
|
|
1702
1721
|
continue;
|
|
1703
1722
|
}
|
|
1704
1723
|
headers.append(
|
|
@@ -1706,6 +1725,13 @@ function withAuthCookiePersistence(handler, options = {}) {
|
|
|
1706
1725
|
action.type === "apply" && action.mode === "session" ? makeSessionCookie(setCookie, authCookieNames) : setCookie
|
|
1707
1726
|
);
|
|
1708
1727
|
}
|
|
1728
|
+
if (action.type === "clear" || action.type === "setup") {
|
|
1729
|
+
for (const name of authCookieNames) {
|
|
1730
|
+
if (!clearedAuthCookies.has(name)) {
|
|
1731
|
+
headers.append("set-cookie", clearedAuthCookie(name, secure));
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1709
1735
|
headers.append(
|
|
1710
1736
|
"set-cookie",
|
|
1711
1737
|
action.type === "clear" || action.type === "setup" ? clearedRememberCookie(rememberCookieName, secure) : rememberCookie(rememberCookieName, action.mode, secure, maxAgeSeconds)
|
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';
|
|
@@ -705,7 +705,6 @@ declare class UserValidator {
|
|
|
705
705
|
* Check if user exists by email
|
|
706
706
|
*/
|
|
707
707
|
checkUserExistsByEmail(email: string): Promise<{
|
|
708
|
-
password: string;
|
|
709
708
|
id: string;
|
|
710
709
|
name: string;
|
|
711
710
|
createdAt: string;
|
|
@@ -714,8 +713,9 @@ declare class UserValidator {
|
|
|
714
713
|
emailVerified: boolean;
|
|
715
714
|
phone: string;
|
|
716
715
|
phoneVerified: boolean;
|
|
716
|
+
password: string;
|
|
717
717
|
image: string;
|
|
718
|
-
status: "active" | "
|
|
718
|
+
status: "active" | "inactive" | "pending";
|
|
719
719
|
roleId: string;
|
|
720
720
|
lastLogin: string;
|
|
721
721
|
failedLoginAttempts: number;
|
|
@@ -727,7 +727,6 @@ declare class UserValidator {
|
|
|
727
727
|
* Check if email exists in database
|
|
728
728
|
*/
|
|
729
729
|
checkEmailExists(email: string): Promise<{
|
|
730
|
-
password: string;
|
|
731
730
|
id: string;
|
|
732
731
|
name: string;
|
|
733
732
|
createdAt: string;
|
|
@@ -736,8 +735,9 @@ declare class UserValidator {
|
|
|
736
735
|
emailVerified: boolean;
|
|
737
736
|
phone: string;
|
|
738
737
|
phoneVerified: boolean;
|
|
738
|
+
password: string;
|
|
739
739
|
image: string;
|
|
740
|
-
status: "active" | "
|
|
740
|
+
status: "active" | "inactive" | "pending";
|
|
741
741
|
roleId: string;
|
|
742
742
|
lastLogin: string;
|
|
743
743
|
failedLoginAttempts: number;
|
|
@@ -1260,8 +1260,8 @@ declare const createUserDto: z.ZodObject<{
|
|
|
1260
1260
|
emailVerified: z.ZodDefault<z.ZodBoolean>;
|
|
1261
1261
|
status: z.ZodOptional<z.ZodEnum<{
|
|
1262
1262
|
active: "active";
|
|
1263
|
-
pending: "pending";
|
|
1264
1263
|
inactive: "inactive";
|
|
1264
|
+
pending: "pending";
|
|
1265
1265
|
}>>;
|
|
1266
1266
|
}, z.core.$strip>;
|
|
1267
1267
|
declare const updateUserDto: z.ZodObject<{
|
|
@@ -1273,8 +1273,8 @@ declare const updateUserDto: z.ZodObject<{
|
|
|
1273
1273
|
emailVerified: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
1274
1274
|
status: z.ZodOptional<z.ZodOptional<z.ZodEnum<{
|
|
1275
1275
|
active: "active";
|
|
1276
|
-
pending: "pending";
|
|
1277
1276
|
inactive: "inactive";
|
|
1277
|
+
pending: "pending";
|
|
1278
1278
|
}>>>;
|
|
1279
1279
|
}, z.core.$strip>;
|
|
1280
1280
|
declare const registerDto: z.ZodObject<{
|
|
@@ -1655,7 +1655,6 @@ declare class AuthController {
|
|
|
1655
1655
|
registerUser(body: RegisterDto): Promise<SanitizedUser>;
|
|
1656
1656
|
loginUser(body: LoginDto): Promise<LoginResult>;
|
|
1657
1657
|
inviteUser(body: InviteUserDto): Promise<Omit<{
|
|
1658
|
-
password: string;
|
|
1659
1658
|
id: string;
|
|
1660
1659
|
name: string;
|
|
1661
1660
|
createdAt: string;
|
|
@@ -1664,8 +1663,9 @@ declare class AuthController {
|
|
|
1664
1663
|
emailVerified: boolean;
|
|
1665
1664
|
phone: string;
|
|
1666
1665
|
phoneVerified: boolean;
|
|
1666
|
+
password: string;
|
|
1667
1667
|
image: string;
|
|
1668
|
-
status: "active" | "
|
|
1668
|
+
status: "active" | "inactive" | "pending";
|
|
1669
1669
|
roleId: string;
|
|
1670
1670
|
lastLogin: string;
|
|
1671
1671
|
failedLoginAttempts: number;
|
|
@@ -1688,7 +1688,6 @@ declare class AuthController {
|
|
|
1688
1688
|
message: string;
|
|
1689
1689
|
}>;
|
|
1690
1690
|
userProfile(authorization?: string): Promise<Omit<{
|
|
1691
|
-
password: string;
|
|
1692
1691
|
id: string;
|
|
1693
1692
|
name: string;
|
|
1694
1693
|
createdAt: string;
|
|
@@ -1697,8 +1696,9 @@ declare class AuthController {
|
|
|
1697
1696
|
emailVerified: boolean;
|
|
1698
1697
|
phone: string;
|
|
1699
1698
|
phoneVerified: boolean;
|
|
1699
|
+
password: string;
|
|
1700
1700
|
image: string;
|
|
1701
|
-
status: "active" | "
|
|
1701
|
+
status: "active" | "inactive" | "pending";
|
|
1702
1702
|
roleId: string;
|
|
1703
1703
|
lastLogin: string;
|
|
1704
1704
|
failedLoginAttempts: number;
|
|
@@ -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({
|
|
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/dist/schema/pg.d.ts
CHANGED
|
@@ -235,7 +235,7 @@ declare const usersTable: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
235
235
|
tableName: "users";
|
|
236
236
|
dataType: "string";
|
|
237
237
|
columnType: "PgEnumColumn";
|
|
238
|
-
data: "active" | "
|
|
238
|
+
data: "active" | "inactive" | "pending";
|
|
239
239
|
driverParam: string;
|
|
240
240
|
notNull: false;
|
|
241
241
|
hasDefault: true;
|
|
@@ -1308,7 +1308,7 @@ declare const authSchema: {
|
|
|
1308
1308
|
tableName: "users";
|
|
1309
1309
|
dataType: "string";
|
|
1310
1310
|
columnType: "PgEnumColumn";
|
|
1311
|
-
data: "active" | "
|
|
1311
|
+
data: "active" | "inactive" | "pending";
|
|
1312
1312
|
driverParam: string;
|
|
1313
1313
|
notNull: false;
|
|
1314
1314
|
hasDefault: true;
|
package/dist/schema/sqlite.d.ts
CHANGED
|
@@ -252,7 +252,7 @@ declare const usersTable: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
|
|
252
252
|
tableName: "users";
|
|
253
253
|
dataType: "string";
|
|
254
254
|
columnType: "SQLiteText";
|
|
255
|
-
data: "active" | "
|
|
255
|
+
data: "active" | "inactive" | "pending";
|
|
256
256
|
driverParam: string;
|
|
257
257
|
notNull: false;
|
|
258
258
|
hasDefault: true;
|
|
@@ -265,7 +265,7 @@ declare const usersTable: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
|
|
265
265
|
generated: undefined;
|
|
266
266
|
}, {}, {
|
|
267
267
|
length: number;
|
|
268
|
-
$type: "active" | "
|
|
268
|
+
$type: "active" | "inactive" | "pending";
|
|
269
269
|
}>;
|
|
270
270
|
roleId: drizzle_orm_sqlite_core.SQLiteColumn<{
|
|
271
271
|
name: "role_id";
|
|
@@ -1476,7 +1476,7 @@ declare const authSchema: {
|
|
|
1476
1476
|
tableName: "users";
|
|
1477
1477
|
dataType: "string";
|
|
1478
1478
|
columnType: "SQLiteText";
|
|
1479
|
-
data: "active" | "
|
|
1479
|
+
data: "active" | "inactive" | "pending";
|
|
1480
1480
|
driverParam: string;
|
|
1481
1481
|
notNull: false;
|
|
1482
1482
|
hasDefault: true;
|
|
@@ -1489,7 +1489,7 @@ declare const authSchema: {
|
|
|
1489
1489
|
generated: undefined;
|
|
1490
1490
|
}, {}, {
|
|
1491
1491
|
length: number;
|
|
1492
|
-
$type: "active" | "
|
|
1492
|
+
$type: "active" | "inactive" | "pending";
|
|
1493
1493
|
}>;
|
|
1494
1494
|
roleId: drizzle_orm_sqlite_core.SQLiteColumn<{
|
|
1495
1495
|
name: "role_id";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "najm-auth",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.5",
|
|
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.
|
|
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",
|