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/README.md +23 -8
- package/dist/client/index.js +1 -1
- package/dist/index.d.ts +75 -9
- package/dist/index.js +435 -369
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -503,11 +503,13 @@ await seedAuthData({
|
|
|
503
503
|
|
|
504
504
|
---
|
|
505
505
|
|
|
506
|
-
## Rate Limiting
|
|
507
|
-
|
|
508
|
-
Auth routes have built-in rate limiting to prevent brute force attacks.
|
|
509
|
-
|
|
510
|
-
|
|
506
|
+
## Rate Limiting
|
|
507
|
+
|
|
508
|
+
Auth routes have built-in rate limiting to prevent brute force attacks.
|
|
509
|
+
The auth plugin registers `najm-rate` as a dependency, so these decorator-level
|
|
510
|
+
limits are active when `auth()` is registered.
|
|
511
|
+
|
|
512
|
+
| Route | Limit | Window | Key Strategy |
|
|
511
513
|
|-------|-------|--------|--------------|
|
|
512
514
|
| `POST /auth/register` | 5 | 15 minutes | IP |
|
|
513
515
|
| `POST /auth/login` | 5 | 15 minutes | IP |
|
|
@@ -575,9 +577,22 @@ throw new HttpError(403, 'Insufficient permissions for this action');
|
|
|
575
577
|
|
|
576
578
|
---
|
|
577
579
|
|
|
578
|
-
## Security Considerations
|
|
579
|
-
|
|
580
|
-
###
|
|
580
|
+
## Security Considerations
|
|
581
|
+
|
|
582
|
+
### Security Defaults
|
|
583
|
+
|
|
584
|
+
- JWT access and refresh secrets are required and must pass minimum strength
|
|
585
|
+
checks.
|
|
586
|
+
- Refresh tokens rotate by session family and suspected family compromise does
|
|
587
|
+
not revoke unrelated user sessions.
|
|
588
|
+
- Password reset and password change revoke existing user sessions.
|
|
589
|
+
- Login uses a dummy password hash for missing users to reduce timing leaks.
|
|
590
|
+
- Forgot-password responses avoid email enumeration.
|
|
591
|
+
- Auth routes register `najm-rate` and ship route-level brute-force limits.
|
|
592
|
+
- Session cookies are signed, short-lived, and checked against session version
|
|
593
|
+
invalidation.
|
|
594
|
+
|
|
595
|
+
### Password Reset Tokens
|
|
581
596
|
|
|
582
597
|
⚠️ **Current behavior:** Reset tokens use JWT expiry (default 1h) for single-use validation. To add database-backed single-use tokens:
|
|
583
598
|
|
package/dist/client/index.js
CHANGED
|
@@ -138,7 +138,7 @@ function decodeToken(token) {
|
|
|
138
138
|
}
|
|
139
139
|
__name(decodeToken, "decodeToken");
|
|
140
140
|
function isTokenExpired(decoded) {
|
|
141
|
-
if (!decoded.exp) return
|
|
141
|
+
if (!decoded.exp) return true;
|
|
142
142
|
return Date.now() / 1e3 >= decoded.exp;
|
|
143
143
|
}
|
|
144
144
|
__name(isTokenExpired, "isTokenExpired");
|
package/dist/index.d.ts
CHANGED
|
@@ -63,6 +63,10 @@ interface AuthConfig {
|
|
|
63
63
|
frontendUrl: string;
|
|
64
64
|
/** Registration mode: 'active' auto-activates, 'pending' requires admin approval (default: 'active') */
|
|
65
65
|
registrationMode: 'active' | 'pending';
|
|
66
|
+
/** When true, users with emailVerified=false are blocked from logging in (default: false) */
|
|
67
|
+
requireVerifiedEmail: boolean;
|
|
68
|
+
/** Cookie path for the refresh token. Scope to the refresh endpoint to limit exposure (default: '/') */
|
|
69
|
+
refreshCookiePath: string;
|
|
66
70
|
/** Per-account lockout settings */
|
|
67
71
|
lockout: LockoutConfig;
|
|
68
72
|
/** Bcrypt work factor (default: 10) */
|
|
@@ -112,6 +116,10 @@ type AuthPluginConfig = {
|
|
|
112
116
|
frontendUrl?: string;
|
|
113
117
|
/** Registration mode: 'active' auto-activates new users, 'pending' requires admin approval (default: 'active') */
|
|
114
118
|
registrationMode?: 'active' | 'pending';
|
|
119
|
+
/** Block login for users whose email is not verified (default: false) */
|
|
120
|
+
requireVerifiedEmail?: boolean;
|
|
121
|
+
/** Cookie path for the refresh token (default: '/'). Set e.g. '/auth' to keep it off unrelated routes. */
|
|
122
|
+
refreshCookiePath?: string;
|
|
115
123
|
/** Per-account lockout settings */
|
|
116
124
|
lockout?: Partial<LockoutConfig>;
|
|
117
125
|
/** Bcrypt work factor (default: 10, valid range: 4-31) */
|
|
@@ -199,7 +207,8 @@ var auth = {
|
|
|
199
207
|
unauthorized: "Unauthorized access",
|
|
200
208
|
sessionExpired: "Session has expired",
|
|
201
209
|
accountLocked: "Account is temporarily locked. Please try again later.",
|
|
202
|
-
accountInactive: "Account is inactive. Please contact support."
|
|
210
|
+
accountInactive: "Account is inactive. Please contact support.",
|
|
211
|
+
emailNotVerified: "Please verify your email address before signing in."
|
|
203
212
|
},
|
|
204
213
|
success: {
|
|
205
214
|
login: "Login successful",
|
|
@@ -242,7 +251,9 @@ var roles = {
|
|
|
242
251
|
notFound: "Role not found",
|
|
243
252
|
exists: "Role already exists",
|
|
244
253
|
nameRequired: "Role name is required",
|
|
245
|
-
cannotDeleteSystem: "Cannot delete system role"
|
|
254
|
+
cannotDeleteSystem: "Cannot delete system role",
|
|
255
|
+
cannotRenameSystem: "Cannot rename the system admin role",
|
|
256
|
+
roleInUse: "Cannot delete a role that is assigned to users"
|
|
246
257
|
},
|
|
247
258
|
success: {
|
|
248
259
|
created: "Role created successfully",
|
|
@@ -302,6 +313,7 @@ declare const AUTH_LOCALES: {
|
|
|
302
313
|
sessionExpired: string;
|
|
303
314
|
accountLocked: string;
|
|
304
315
|
accountInactive: string;
|
|
316
|
+
emailNotVerified: string;
|
|
305
317
|
};
|
|
306
318
|
success: {
|
|
307
319
|
login: string;
|
|
@@ -345,6 +357,8 @@ declare const AUTH_LOCALES: {
|
|
|
345
357
|
exists: string;
|
|
346
358
|
nameRequired: string;
|
|
347
359
|
cannotDeleteSystem: string;
|
|
360
|
+
cannotRenameSystem: string;
|
|
361
|
+
roleInUse: string;
|
|
348
362
|
};
|
|
349
363
|
success: {
|
|
350
364
|
created: string;
|
|
@@ -406,6 +420,12 @@ interface SessionCookieData {
|
|
|
406
420
|
};
|
|
407
421
|
roles: string[];
|
|
408
422
|
permissions: string[];
|
|
423
|
+
/**
|
|
424
|
+
* Per-user session version captured when the cookie was written. Lets the
|
|
425
|
+
* fast-path reader reject a cookie whose session has since been invalidated
|
|
426
|
+
* (password change/reset, logout-all) without hitting the database.
|
|
427
|
+
*/
|
|
428
|
+
sessionVersion: number;
|
|
409
429
|
/** Epoch ms when the cookie was written */
|
|
410
430
|
iat: number;
|
|
411
431
|
}
|
|
@@ -413,6 +433,7 @@ declare class CookieManager {
|
|
|
413
433
|
private config;
|
|
414
434
|
private cookieService;
|
|
415
435
|
private get cookieName();
|
|
436
|
+
private get refreshCookiePath();
|
|
416
437
|
private get sessionCookieName();
|
|
417
438
|
private get sessionMaxAge();
|
|
418
439
|
private get sessionSecret();
|
|
@@ -565,6 +586,9 @@ declare class RoleRepository {
|
|
|
565
586
|
db: TDb;
|
|
566
587
|
private schema;
|
|
567
588
|
private get roles();
|
|
589
|
+
private get users();
|
|
590
|
+
/** True if any user currently references this role (blocks deletion). */
|
|
591
|
+
hasUsers(roleId: string): Promise<boolean>;
|
|
568
592
|
getAll(): Promise<RoleEntity[]>;
|
|
569
593
|
getById(id: string): Promise<RoleEntity | undefined>;
|
|
570
594
|
getByName(name: string): Promise<RoleEntity | undefined>;
|
|
@@ -625,6 +649,7 @@ declare class RoleValidator {
|
|
|
625
649
|
declare class RoleService {
|
|
626
650
|
private roleRepository;
|
|
627
651
|
private roleValidator;
|
|
652
|
+
private t;
|
|
628
653
|
constructor(roleRepository: RoleRepository, roleValidator: RoleValidator);
|
|
629
654
|
getAll(): Promise<{
|
|
630
655
|
id: string;
|
|
@@ -654,14 +679,17 @@ declare class RoleService {
|
|
|
654
679
|
createdAt: string;
|
|
655
680
|
updatedAt: string;
|
|
656
681
|
}>;
|
|
657
|
-
update(id:
|
|
682
|
+
update(id: string, data: {
|
|
683
|
+
name?: string;
|
|
684
|
+
description?: string;
|
|
685
|
+
}): Promise<{
|
|
658
686
|
id: string;
|
|
659
687
|
name: string;
|
|
660
688
|
description: string;
|
|
661
689
|
createdAt: string;
|
|
662
690
|
updatedAt: string;
|
|
663
691
|
}>;
|
|
664
|
-
delete(id:
|
|
692
|
+
delete(id: string): Promise<{
|
|
665
693
|
id: string;
|
|
666
694
|
name: string;
|
|
667
695
|
description: string;
|
|
@@ -831,6 +859,12 @@ declare class TokenService {
|
|
|
831
859
|
getTokenExpire(token: string): number | undefined;
|
|
832
860
|
decodeAccessToken(token: string): JwtPayload | null;
|
|
833
861
|
private signAccessToken;
|
|
862
|
+
/**
|
|
863
|
+
* Current per-user session version (0 when never invalidated). The signed
|
|
864
|
+
* session cookie stamps this so a fast-path reader can reject a cookie whose
|
|
865
|
+
* session was invalidated after it was written.
|
|
866
|
+
*/
|
|
867
|
+
getSessionVersion(userId: string): Promise<number>;
|
|
834
868
|
/**
|
|
835
869
|
* Generate access token with unique jti for blacklist support.
|
|
836
870
|
* Includes roles/permissions for client-side RBAC/PBAC.
|
|
@@ -855,6 +889,7 @@ declare class TokenService {
|
|
|
855
889
|
tokenFamily: string;
|
|
856
890
|
roles: string[];
|
|
857
891
|
permissions: string[];
|
|
892
|
+
sessionVersion: number;
|
|
858
893
|
accessToken: string;
|
|
859
894
|
refreshToken: string;
|
|
860
895
|
accessTokenExpiresAt: number;
|
|
@@ -888,6 +923,7 @@ declare class TokenService {
|
|
|
888
923
|
tokenFamily: string;
|
|
889
924
|
roles: string[];
|
|
890
925
|
permissions: string[];
|
|
926
|
+
sessionVersion: number;
|
|
891
927
|
accessToken: string;
|
|
892
928
|
refreshToken: string;
|
|
893
929
|
accessTokenExpiresAt: number;
|
|
@@ -995,6 +1031,12 @@ declare const updateUserDto: z.ZodObject<{
|
|
|
995
1031
|
inactive: "inactive";
|
|
996
1032
|
}>>>;
|
|
997
1033
|
}, z.core.$strip>;
|
|
1034
|
+
declare const registerDto: z.ZodObject<{
|
|
1035
|
+
name: z.ZodOptional<z.ZodString>;
|
|
1036
|
+
email: z.ZodString;
|
|
1037
|
+
password: z.ZodString;
|
|
1038
|
+
image: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1039
|
+
}, z.core.$strip>;
|
|
998
1040
|
declare const inviteUserDto: z.ZodObject<{
|
|
999
1041
|
name: z.ZodOptional<z.ZodString>;
|
|
1000
1042
|
email: z.ZodString;
|
|
@@ -1038,6 +1080,7 @@ declare const userListQuery: z.ZodObject<{
|
|
|
1038
1080
|
}, z.core.$strip>;
|
|
1039
1081
|
type CreateUserDto = z.infer<typeof createUserDto>;
|
|
1040
1082
|
type UpdateUserDto = z.infer<typeof updateUserDto>;
|
|
1083
|
+
type RegisterDto = z.infer<typeof registerDto>;
|
|
1041
1084
|
type InviteUserDto = z.infer<typeof inviteUserDto>;
|
|
1042
1085
|
type UserIdParam = z.infer<typeof userIdParam>;
|
|
1043
1086
|
type LoginDto = z.infer<typeof loginDto>;
|
|
@@ -1080,7 +1123,7 @@ declare class AuthService {
|
|
|
1080
1123
|
private nextLockoutUntil;
|
|
1081
1124
|
private getDummyHash;
|
|
1082
1125
|
warmupPasswordHash(): Promise<void>;
|
|
1083
|
-
registerUser(body:
|
|
1126
|
+
registerUser(body: RegisterDto): Promise<SanitizedUser>;
|
|
1084
1127
|
/**
|
|
1085
1128
|
* Admin-initiated account creation. The user is created with a random,
|
|
1086
1129
|
* unusable password (the schema requires one) and then emailed a one-time
|
|
@@ -1090,7 +1133,9 @@ declare class AuthService {
|
|
|
1090
1133
|
* Email is best-effort: a send failure logs a warning but never rolls back
|
|
1091
1134
|
* account creation (and with the console provider, nothing is actually sent).
|
|
1092
1135
|
*/
|
|
1093
|
-
inviteUser(body: ProvisionUserInput): Promise<SanitizedUser
|
|
1136
|
+
inviteUser(body: ProvisionUserInput): Promise<SanitizedUser & {
|
|
1137
|
+
emailSent: boolean;
|
|
1138
|
+
}>;
|
|
1094
1139
|
/**
|
|
1095
1140
|
* Create a login for a person record. The branch is intentional and is the
|
|
1096
1141
|
* single rule callers rely on:
|
|
@@ -1147,11 +1192,32 @@ declare class AuthService {
|
|
|
1147
1192
|
declare class AuthController {
|
|
1148
1193
|
private authService;
|
|
1149
1194
|
constructor(authService: AuthService);
|
|
1150
|
-
registerUser(body:
|
|
1195
|
+
registerUser(body: RegisterDto): Promise<SanitizedUser>;
|
|
1151
1196
|
loginUser(body: LoginDto): Promise<TokenPair & {
|
|
1152
1197
|
user: SanitizedUser;
|
|
1153
1198
|
}>;
|
|
1154
|
-
inviteUser(body: InviteUserDto): Promise<
|
|
1199
|
+
inviteUser(body: InviteUserDto): Promise<Omit<{
|
|
1200
|
+
id: string;
|
|
1201
|
+
name: string;
|
|
1202
|
+
createdAt: string;
|
|
1203
|
+
updatedAt: string;
|
|
1204
|
+
email: string;
|
|
1205
|
+
emailVerified: boolean;
|
|
1206
|
+
phone: string;
|
|
1207
|
+
phoneVerified: boolean;
|
|
1208
|
+
password: string;
|
|
1209
|
+
image: string;
|
|
1210
|
+
status: "active" | "pending" | "inactive";
|
|
1211
|
+
roleId: string;
|
|
1212
|
+
lastLogin: string;
|
|
1213
|
+
failedLoginAttempts: number;
|
|
1214
|
+
lockoutUntil: string;
|
|
1215
|
+
}, "password" | "failedLoginAttempts" | "lockoutUntil"> & {
|
|
1216
|
+
role?: string | null;
|
|
1217
|
+
permissions?: string[];
|
|
1218
|
+
} & {
|
|
1219
|
+
emailSent: boolean;
|
|
1220
|
+
}>;
|
|
1155
1221
|
refreshTokens(): Promise<TokenPair>;
|
|
1156
1222
|
logoutUser(userId: string, authorization?: string): Promise<{
|
|
1157
1223
|
data: any;
|
|
@@ -2131,4 +2197,4 @@ declare const authSeed: (config: AuthSeedConfig) => Record<string, SeedEntry>;
|
|
|
2131
2197
|
*/
|
|
2132
2198
|
declare function seedAuthData(config: SeedAuthDataConfig): Promise<SeedAuthDataResult>;
|
|
2133
2199
|
|
|
2134
|
-
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 InviteUserDto, 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, type ProvisionUserInput, 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 RunAsUser, type SanitizedUser, ScopeContext, type ScopeResult, type SeedAuthDataConfig, type SeedAuthDataResult, type SeedUserConfig, type SessionCookieData, 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, type UserListQuery, 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, inviteUserDto, isAdmin, isAdministrator, isAuth, isEmpty, isFile, isPath, join, languageParam, loginDto, own, parseSchema, permissionIdParam, pickProps, refreshTokenDto, resetPasswordDto, revokeTokenDto, roleIdParam, runAsUser, seedAuthData, tokenIdParam, updatePermissionDto, updateRoleDto, updateTokenDto, updateUserDto, userIdInParam, userIdParam, userListQuery, verifyTokenDto, where };
|
|
2200
|
+
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 InviteUserDto, 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, type ProvisionUserInput, ROLES, ROLE_GROUPS, type RefreshTokenDto, type RegisterDto, 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 RunAsUser, type SanitizedUser, ScopeContext, type ScopeResult, type SeedAuthDataConfig, type SeedAuthDataResult, type SeedUserConfig, type SessionCookieData, 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, type UserListQuery, 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, inviteUserDto, isAdmin, isAdministrator, isAuth, isEmpty, isFile, isPath, join, languageParam, loginDto, own, parseSchema, permissionIdParam, pickProps, refreshTokenDto, registerDto, resetPasswordDto, revokeTokenDto, roleIdParam, runAsUser, seedAuthData, tokenIdParam, updatePermissionDto, updateRoleDto, updateTokenDto, updateUserDto, userIdInParam, userIdParam, userListQuery, verifyTokenDto, where };
|