@naman_deep_singh/security 1.3.3 → 1.4.0

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.
Files changed (46) hide show
  1. package/README.md +153 -355
  2. package/dist/cjs/core/crypto/cryptoManager.js +34 -17
  3. package/dist/cjs/core/jwt/decode.js +4 -1
  4. package/dist/cjs/core/jwt/generateTokens.js +4 -1
  5. package/dist/cjs/core/jwt/jwtManager.d.ts +19 -43
  6. package/dist/cjs/core/jwt/jwtManager.js +72 -206
  7. package/dist/cjs/core/jwt/parseDuration.js +3 -2
  8. package/dist/cjs/core/jwt/signToken.js +2 -1
  9. package/dist/cjs/core/jwt/validateToken.d.ts +10 -7
  10. package/dist/cjs/core/jwt/validateToken.js +14 -11
  11. package/dist/cjs/core/jwt/verify.d.ts +9 -10
  12. package/dist/cjs/core/jwt/verify.js +57 -14
  13. package/dist/cjs/core/password/hash.js +2 -2
  14. package/dist/cjs/core/password/passwordManager.d.ts +1 -1
  15. package/dist/cjs/core/password/passwordManager.js +35 -87
  16. package/dist/cjs/core/password/strength.js +5 -5
  17. package/dist/cjs/core/password/utils.d.ts +12 -0
  18. package/dist/cjs/core/password/utils.js +16 -1
  19. package/dist/cjs/core/password/verify.js +4 -4
  20. package/dist/cjs/index.d.ts +2 -7
  21. package/dist/esm/core/crypto/cryptoManager.js +34 -17
  22. package/dist/esm/core/jwt/decode.js +4 -1
  23. package/dist/esm/core/jwt/generateTokens.js +4 -1
  24. package/dist/esm/core/jwt/jwtManager.d.ts +19 -43
  25. package/dist/esm/core/jwt/jwtManager.js +73 -207
  26. package/dist/esm/core/jwt/parseDuration.js +3 -2
  27. package/dist/esm/core/jwt/signToken.js +2 -1
  28. package/dist/esm/core/jwt/validateToken.d.ts +10 -7
  29. package/dist/esm/core/jwt/validateToken.js +14 -11
  30. package/dist/esm/core/jwt/verify.d.ts +9 -10
  31. package/dist/esm/core/jwt/verify.js +55 -12
  32. package/dist/esm/core/password/hash.js +2 -2
  33. package/dist/esm/core/password/passwordManager.d.ts +1 -1
  34. package/dist/esm/core/password/passwordManager.js +35 -87
  35. package/dist/esm/core/password/strength.js +5 -5
  36. package/dist/esm/core/password/utils.d.ts +12 -0
  37. package/dist/esm/core/password/utils.js +16 -1
  38. package/dist/esm/core/password/verify.js +4 -4
  39. package/dist/esm/index.d.ts +2 -7
  40. package/dist/types/core/jwt/jwtManager.d.ts +19 -43
  41. package/dist/types/core/jwt/validateToken.d.ts +10 -7
  42. package/dist/types/core/jwt/verify.d.ts +9 -10
  43. package/dist/types/core/password/passwordManager.d.ts +1 -1
  44. package/dist/types/core/password/utils.d.ts +12 -0
  45. package/dist/types/index.d.ts +2 -7
  46. package/package.json +2 -2
@@ -2,34 +2,37 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validateTokenPayload = validateTokenPayload;
4
4
  exports.isTokenExpired = isTokenExpired;
5
- function validateTokenPayload(payload, rules = {
6
- requiredFields: ['exp', 'iat'],
7
- }) {
8
- const { requiredFields = [], forbiddenFields = [], validateTypes = {}, } = rules;
5
+ const errors_utils_1 = require("@naman_deep_singh/errors-utils");
6
+ /**
7
+ * Validates a JWT payload according to the provided rules.
8
+ * Throws ValidationError if validation fails.
9
+ */
10
+ function validateTokenPayload(payload, rules = { requiredFields: ['exp', 'iat'] }) {
11
+ const { requiredFields = [], forbiddenFields = [], validateTypes = {} } = rules;
9
12
  // 1. Required fields
10
13
  for (const field of requiredFields) {
11
14
  if (!(field in payload)) {
12
- return { valid: false, error: `Missing required field: ${field}` };
15
+ throw new errors_utils_1.ValidationError(`Missing required field: ${field}`);
13
16
  }
14
17
  }
15
18
  // 2. Forbidden fields
16
19
  for (const field of forbiddenFields) {
17
20
  if (field in payload) {
18
- return { valid: false, error: `Forbidden field in token: ${field}` };
21
+ throw new errors_utils_1.ValidationError(`Forbidden field in token: ${field}`);
19
22
  }
20
23
  }
21
24
  // 3. Type validation
22
25
  for (const key in validateTypes) {
23
26
  const expectedType = validateTypes[key];
24
27
  if (key in payload && typeof payload[key] !== expectedType) {
25
- return {
26
- valid: false,
27
- error: `Invalid type for ${key}. Expected ${expectedType}.`,
28
- };
28
+ throw new errors_utils_1.ValidationError(`Invalid type for ${key}. Expected ${expectedType}, got ${typeof payload[key]}`);
29
29
  }
30
30
  }
31
- return { valid: true };
32
31
  }
32
+ /**
33
+ * Checks if a JWT payload is expired.
34
+ * Returns true if expired or missing 'exp'.
35
+ */
33
36
  function isTokenExpired(payload) {
34
37
  if (!payload.exp)
35
38
  return true;
@@ -1,19 +1,18 @@
1
- import type jwt from 'jsonwebtoken';
2
- import { type JwtPayload, type Secret } from 'jsonwebtoken';
3
- import type { VerificationResult } from './types';
1
+ import { type JwtPayload, type Secret, VerifyOptions } from 'jsonwebtoken';
2
+ import { VerificationResult } from './types';
4
3
  /**
5
- * Verify token (throws if invalid or expired)
4
+ * Verify token (throws UnauthorizedError if invalid or expired)
6
5
  */
7
6
  export declare const verifyToken: (token: string, secret: Secret) => string | JwtPayload;
8
7
  /**
9
- * Safe verify never throws, returns structured result
8
+ * Verify token with options
10
9
  */
11
- export declare const safeVerifyToken: (token: string, secret: Secret) => VerificationResult;
10
+ export declare const verifyTokenWithOptions: (token: string, secret: Secret, options?: VerifyOptions) => string | JwtPayload;
12
11
  /**
13
- * Verify token with validation options
12
+ * Safe verify — never throws, returns structured result with UnauthorizedError on failure
14
13
  */
15
- export declare const verifyTokenWithOptions: (token: string, secret: Secret, options?: jwt.VerifyOptions) => string | JwtPayload;
14
+ export declare const safeVerifyToken: (token: string, secret: Secret) => VerificationResult;
16
15
  /**
17
- * Safe verify with validation options
16
+ * Safe verify with options — never throws, returns structured result with UnauthorizedError on failure
18
17
  */
19
- export declare const safeVerifyTokenWithOptions: (token: string, secret: Secret, options?: jwt.VerifyOptions) => VerificationResult;
18
+ export declare const safeVerifyTokenWithOptions: (token: string, secret: Secret, options?: VerifyOptions) => VerificationResult;
@@ -1,16 +1,46 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.safeVerifyTokenWithOptions = exports.verifyTokenWithOptions = exports.safeVerifyToken = exports.verifyToken = void 0;
3
+ exports.safeVerifyTokenWithOptions = exports.safeVerifyToken = exports.verifyTokenWithOptions = exports.verifyToken = void 0;
4
4
  const jsonwebtoken_1 = require("jsonwebtoken");
5
+ const errors_utils_1 = require("@naman_deep_singh/errors-utils");
5
6
  /**
6
- * Verify token (throws if invalid or expired)
7
+ * Verify token (throws UnauthorizedError if invalid or expired)
7
8
  */
8
9
  const verifyToken = (token, secret) => {
9
- return (0, jsonwebtoken_1.verify)(token, secret);
10
+ try {
11
+ return (0, jsonwebtoken_1.verify)(token, secret);
12
+ }
13
+ catch (error) {
14
+ if (error.name === 'TokenExpiredError') {
15
+ throw new errors_utils_1.UnauthorizedError({ message: 'Token has expired' }, error);
16
+ }
17
+ if (error.name === 'JsonWebTokenError') {
18
+ throw new errors_utils_1.UnauthorizedError({ message: 'Invalid token' }, error);
19
+ }
20
+ throw new errors_utils_1.UnauthorizedError({ message: 'Failed to verify token' }, error);
21
+ }
10
22
  };
11
23
  exports.verifyToken = verifyToken;
12
24
  /**
13
- * Safe verify never throws, returns structured result
25
+ * Verify token with options
26
+ */
27
+ const verifyTokenWithOptions = (token, secret, options = {}) => {
28
+ try {
29
+ return (0, jsonwebtoken_1.verify)(token, secret, options);
30
+ }
31
+ catch (error) {
32
+ if (error.name === 'TokenExpiredError') {
33
+ throw new errors_utils_1.UnauthorizedError({ message: 'Token has expired' }, error);
34
+ }
35
+ if (error.name === 'JsonWebTokenError') {
36
+ throw new errors_utils_1.UnauthorizedError({ message: 'Invalid token' }, error);
37
+ }
38
+ throw new errors_utils_1.UnauthorizedError({ message: 'Failed to verify token' }, error);
39
+ }
40
+ };
41
+ exports.verifyTokenWithOptions = verifyTokenWithOptions;
42
+ /**
43
+ * Safe verify — never throws, returns structured result with UnauthorizedError on failure
14
44
  */
15
45
  const safeVerifyToken = (token, secret) => {
16
46
  try {
@@ -18,19 +48,22 @@ const safeVerifyToken = (token, secret) => {
18
48
  return { valid: true, payload: decoded };
19
49
  }
20
50
  catch (error) {
21
- return { valid: false, error: error };
51
+ let wrappedError;
52
+ if (error.name === 'TokenExpiredError') {
53
+ wrappedError = new errors_utils_1.UnauthorizedError({ message: 'Token has expired' }, error);
54
+ }
55
+ else if (error.name === 'JsonWebTokenError') {
56
+ wrappedError = new errors_utils_1.UnauthorizedError({ message: 'Invalid token' }, error);
57
+ }
58
+ else {
59
+ wrappedError = new errors_utils_1.UnauthorizedError({ message: 'Failed to verify token' }, error);
60
+ }
61
+ return { valid: false, error: wrappedError };
22
62
  }
23
63
  };
24
64
  exports.safeVerifyToken = safeVerifyToken;
25
65
  /**
26
- * Verify token with validation options
27
- */
28
- const verifyTokenWithOptions = (token, secret, options = {}) => {
29
- return (0, jsonwebtoken_1.verify)(token, secret, options);
30
- };
31
- exports.verifyTokenWithOptions = verifyTokenWithOptions;
32
- /**
33
- * Safe verify with validation options
66
+ * Safe verify with options — never throws, returns structured result with UnauthorizedError on failure
34
67
  */
35
68
  const safeVerifyTokenWithOptions = (token, secret, options = {}) => {
36
69
  try {
@@ -38,7 +71,17 @@ const safeVerifyTokenWithOptions = (token, secret, options = {}) => {
38
71
  return { valid: true, payload: decoded };
39
72
  }
40
73
  catch (error) {
41
- return { valid: false, error: error };
74
+ let wrappedError;
75
+ if (error.name === 'TokenExpiredError') {
76
+ wrappedError = new errors_utils_1.UnauthorizedError({ message: 'Token has expired' }, error);
77
+ }
78
+ else if (error.name === 'JsonWebTokenError') {
79
+ wrappedError = new errors_utils_1.UnauthorizedError({ message: 'Invalid token' }, error);
80
+ }
81
+ else {
82
+ wrappedError = new errors_utils_1.UnauthorizedError({ message: 'Failed to verify token' }, error);
83
+ }
84
+ return { valid: false, error: wrappedError };
42
85
  }
43
86
  };
44
87
  exports.safeVerifyTokenWithOptions = safeVerifyTokenWithOptions;
@@ -19,7 +19,7 @@ const hashPassword = async (password, saltRounds = 10) => {
19
19
  return bcryptjs_1.default.hash(password, salt);
20
20
  }
21
21
  catch (_err) {
22
- throw new errors_utils_1.InternalServerError('Password hashing failed');
22
+ throw new errors_utils_1.InternalServerError({ message: 'Password hashing failed' });
23
23
  }
24
24
  };
25
25
  exports.hashPassword = hashPassword;
@@ -36,7 +36,7 @@ const hashPasswordSync = (password, saltRounds = 10) => {
36
36
  return bcryptjs_1.default.hashSync(password, salt);
37
37
  }
38
38
  catch (_error) {
39
- throw new errors_utils_1.InternalServerError('Password hashing failed');
39
+ throw new errors_utils_1.InternalServerError({ message: 'Password hashing failed' });
40
40
  }
41
41
  };
42
42
  exports.hashPasswordSync = hashPasswordSync;
@@ -23,7 +23,7 @@ export declare class PasswordManager implements IPasswordManager {
23
23
  */
24
24
  checkStrength(password: string): PasswordStrength;
25
25
  /**
26
- * Check if password hash needs upgrade (different salt rounds)
26
+ * Check if password hash needs upgrade (saltRounds change)
27
27
  */
28
28
  needsUpgrade(_hash: string, _currentConfig: PasswordConfig): boolean;
29
29
  }
@@ -27,25 +27,20 @@ class PasswordManager {
27
27
  async hash(password, salt) {
28
28
  try {
29
29
  (0, utils_1.ensureValidPassword)(password);
30
- // Validate password meets basic requirements
31
30
  this.validate(password);
32
31
  const saltRounds = this.defaultConfig.saltRounds;
33
- let passwordSalt = salt;
34
- if (!passwordSalt) {
35
- passwordSalt = await bcryptjs_1.default.genSalt(saltRounds);
32
+ let finalSalt = salt;
33
+ if (!finalSalt) {
34
+ finalSalt = await bcryptjs_1.default.genSalt(saltRounds);
36
35
  }
37
- const hash = await bcryptjs_1.default.hash(password, passwordSalt);
38
- return {
39
- hash,
40
- salt: passwordSalt,
41
- };
36
+ const hash = await bcryptjs_1.default.hash(password, finalSalt);
37
+ return { hash, salt: finalSalt };
42
38
  }
43
39
  catch (error) {
44
- if (error instanceof errors_utils_1.BadRequestError ||
45
- error instanceof errors_utils_1.ValidationError) {
40
+ if (error instanceof errors_utils_1.BadRequestError || error instanceof errors_utils_1.ValidationError) {
46
41
  throw error;
47
42
  }
48
- throw new errors_utils_1.BadRequestError('Failed to hash password');
43
+ throw new errors_utils_1.BadRequestError({ message: 'Failed to hash password' }, error instanceof Error ? error : undefined);
49
44
  }
50
45
  }
51
46
  /**
@@ -53,19 +48,12 @@ class PasswordManager {
53
48
  */
54
49
  async verify(password, hash, salt) {
55
50
  try {
56
- if (!password || !hash || !salt) {
51
+ if (!password || !hash || !salt)
57
52
  return false;
58
- }
59
- // First verify with the provided salt
60
- const isValid = await bcryptjs_1.default.compare(password, hash);
61
- // If invalid and different salt was used, try regenerating hash with new salt
62
- if (!isValid && salt !== this.defaultConfig.saltRounds?.toString()) {
63
- const newHash = await bcryptjs_1.default.hash(password, salt);
64
- return newHash === hash;
65
- }
66
- return isValid;
53
+ // bcrypt compare works directly with hash
54
+ return await bcryptjs_1.default.compare(password, hash);
67
55
  }
68
- catch (_error) {
56
+ catch {
69
57
  return false;
70
58
  }
71
59
  }
@@ -75,7 +63,7 @@ class PasswordManager {
75
63
  generate(length = 16, options = {}) {
76
64
  const config = { ...this.defaultConfig, ...options };
77
65
  if (length < config.minLength || length > config.maxLength) {
78
- throw new errors_utils_1.ValidationError(`Password length must be between ${config.minLength} and ${config.maxLength}`);
66
+ throw new errors_utils_1.ValidationError({ message: `Password length must be between ${config.minLength} and ${config.maxLength}` });
79
67
  }
80
68
  let charset = 'abcdefghijklmnopqrstuvwxyz';
81
69
  if (config.requireUppercase)
@@ -84,24 +72,20 @@ class PasswordManager {
84
72
  charset += '0123456789';
85
73
  if (config.requireSpecialChars)
86
74
  charset += '!@#$%^&*()_+-=[]{}|;:,.<>?';
87
- let password = '';
88
75
  const randomBytes = crypto_1.default.randomBytes(length);
76
+ let password = '';
89
77
  for (let i = 0; i < length; i++) {
90
78
  password += charset[randomBytes[i] % charset.length];
91
79
  }
92
- // Ensure all requirements are met
93
- if (config.requireUppercase && !/[A-Z]/.test(password)) {
80
+ // Ensure requirements
81
+ if (config.requireUppercase && !/[A-Z]/.test(password))
94
82
  password = password.replace(/[a-z]/, 'A');
95
- }
96
- if (config.requireLowercase && !/[a-z]/.test(password)) {
83
+ if (config.requireLowercase && !/[a-z]/.test(password))
97
84
  password = password.replace(/[A-Z]/, 'a');
98
- }
99
- if (config.requireNumbers && !/[0-9]/.test(password)) {
85
+ if (config.requireNumbers && !/[0-9]/.test(password))
100
86
  password = password.replace(/[A-Za-z]/, '0');
101
- }
102
- if (config.requireSpecialChars && !/[^A-Za-z0-9]/.test(password)) {
87
+ if (config.requireSpecialChars && !/[^A-Za-z0-9]/.test(password))
103
88
  password = password.replace(/[A-Za-z0-9]/, '!');
104
- }
105
89
  return password;
106
90
  }
107
91
  /**
@@ -110,45 +94,27 @@ class PasswordManager {
110
94
  validate(password, config = {}) {
111
95
  const finalConfig = { ...this.defaultConfig, ...config };
112
96
  const errors = [];
113
- // Basic validation
114
- if (!password || typeof password !== 'string') {
97
+ if (!password || typeof password !== 'string')
115
98
  errors.push('Password must be a non-empty string');
116
- }
117
- // Length validation
118
- if (password.length < finalConfig.minLength) {
119
- errors.push(`Password must be at least ${finalConfig.minLength} characters long`);
120
- }
121
- if (password.length > finalConfig.maxLength) {
99
+ if (password.length < finalConfig.minLength)
100
+ errors.push(`Password must be at least ${finalConfig.minLength} characters`);
101
+ if (password.length > finalConfig.maxLength)
122
102
  errors.push(`Password must not exceed ${finalConfig.maxLength} characters`);
123
- }
124
- // Complexity requirements
125
- if (finalConfig.requireUppercase && !/[A-Z]/.test(password)) {
103
+ if (finalConfig.requireUppercase && !/[A-Z]/.test(password))
126
104
  errors.push('Password must contain at least one uppercase letter');
127
- }
128
- if (finalConfig.requireLowercase && !/[a-z]/.test(password)) {
105
+ if (finalConfig.requireLowercase && !/[a-z]/.test(password))
129
106
  errors.push('Password must contain at least one lowercase letter');
130
- }
131
- if (finalConfig.requireNumbers && !/[0-9]/.test(password)) {
107
+ if (finalConfig.requireNumbers && !/[0-9]/.test(password))
132
108
  errors.push('Password must contain at least one number');
133
- }
134
- if (finalConfig.requireSpecialChars && !/[^A-Za-z0-9]/.test(password)) {
109
+ if (finalConfig.requireSpecialChars && !/[^A-Za-z0-9]/.test(password))
135
110
  errors.push('Password must contain at least one special character');
136
- }
137
- // Custom rules
138
111
  if (finalConfig.customRules) {
139
112
  finalConfig.customRules.forEach((rule) => {
140
- if (!rule.test(password)) {
113
+ if (!rule.test(password))
141
114
  errors.push(rule.message);
142
- }
143
115
  });
144
116
  }
145
- const strength = this.checkStrength(password);
146
- const isValid = errors.length === 0;
147
- return {
148
- isValid,
149
- errors,
150
- strength,
151
- };
117
+ return { isValid: errors.length === 0, errors, strength: this.checkStrength(password) };
152
118
  }
153
119
  /**
154
120
  * Check password strength
@@ -158,26 +124,20 @@ class PasswordManager {
158
124
  let score = 0;
159
125
  const feedback = [];
160
126
  const suggestions = [];
161
- /* ---------------- Entropy baseline ---------------- */
162
127
  if (entropy < 28) {
163
128
  feedback.push('Password is easy to guess');
164
129
  suggestions.push('Use more unique characters and length');
165
130
  }
166
- else if (entropy < 36) {
167
- score += 1;
168
- }
169
- else if (entropy < 60) {
131
+ else if (entropy < 36)
132
+ score++;
133
+ else if (entropy < 60)
170
134
  score += 2;
171
- }
172
- else {
135
+ else
173
136
  score += 3;
174
- }
175
- /* ---------------- Length scoring ---------------- */
176
137
  if (password.length >= 12)
177
138
  score++;
178
139
  if (password.length >= 16)
179
140
  score++;
180
- /* ---------------- Character variety ---------------- */
181
141
  if (/[a-z]/.test(password))
182
142
  score++;
183
143
  if (/[A-Z]/.test(password))
@@ -186,7 +146,6 @@ class PasswordManager {
186
146
  score++;
187
147
  if (/[^A-Za-z0-9]/.test(password))
188
148
  score++;
189
- /* ---------------- Pattern deductions ---------------- */
190
149
  if (/^[A-Za-z]+$/.test(password)) {
191
150
  score--;
192
151
  feedback.push('Consider adding numbers or symbols');
@@ -203,15 +162,12 @@ class PasswordManager {
203
162
  score--;
204
163
  feedback.push('Avoid sequential patterns');
205
164
  }
206
- /* ---------------- Common passwords ---------------- */
207
165
  const commonPasswords = ['password', '123456', 'qwerty', 'admin', 'letmein'];
208
166
  if (commonPasswords.some((common) => password.toLowerCase().includes(common))) {
209
167
  score = 0;
210
168
  feedback.push('Avoid common passwords');
211
169
  }
212
- /* ---------------- Clamp score ---------------- */
213
170
  score = Math.max(0, Math.min(4, score));
214
- /* ---------------- Strength label ---------------- */
215
171
  let label;
216
172
  switch (score) {
217
173
  case 0:
@@ -234,22 +190,14 @@ class PasswordManager {
234
190
  label = 'strong';
235
191
  suggestions.push('Your password is very secure');
236
192
  break;
237
- default:
238
- label = 'very-weak';
193
+ default: label = 'very-weak';
239
194
  }
240
- return {
241
- score,
242
- label,
243
- feedback,
244
- suggestions,
245
- };
195
+ return { score, label, feedback, suggestions };
246
196
  }
247
197
  /**
248
- * Check if password hash needs upgrade (different salt rounds)
198
+ * Check if password hash needs upgrade (saltRounds change)
249
199
  */
250
200
  needsUpgrade(_hash, _currentConfig) {
251
- // Simple heuristic: if the hash doesn't match current salt rounds pattern
252
- // In practice, you'd need to store the salt rounds with the hash
253
201
  return false;
254
202
  }
255
203
  }
@@ -4,18 +4,18 @@ exports.isPasswordStrong = void 0;
4
4
  const errors_utils_1 = require("@naman_deep_singh/errors-utils");
5
5
  const isPasswordStrong = (password, options = {}) => {
6
6
  if (!password)
7
- throw new errors_utils_1.BadRequestError('Invalid password provided');
7
+ throw new errors_utils_1.BadRequestError({ message: 'Invalid password provided' });
8
8
  const { minLength = 8, requireUppercase = true, requireLowercase = true, requireNumbers = true, requireSymbols = false, } = options;
9
9
  if (password.length < minLength)
10
10
  throw new errors_utils_1.ValidationError(`Password must be at least ${minLength} characters`);
11
11
  if (requireUppercase && !/[A-Z]/.test(password))
12
- throw new errors_utils_1.ValidationError('Password must include uppercase letters');
12
+ throw new errors_utils_1.ValidationError({ message: 'Password must include uppercase letters' });
13
13
  if (requireLowercase && !/[a-z]/.test(password))
14
- throw new errors_utils_1.ValidationError('Password must include lowercase letters');
14
+ throw new errors_utils_1.ValidationError({ message: 'Password must include lowercase letters' });
15
15
  if (requireNumbers && !/[0-9]/.test(password))
16
- throw new errors_utils_1.ValidationError('Password must include numbers');
16
+ throw new errors_utils_1.ValidationError({ message: 'Password must include numbers' });
17
17
  if (requireSymbols && !/[^A-Za-z0-9]/.test(password))
18
- throw new errors_utils_1.ValidationError('Password must include symbols');
18
+ throw new errors_utils_1.ValidationError({ message: 'Password must include symbols' });
19
19
  return true;
20
20
  };
21
21
  exports.isPasswordStrong = isPasswordStrong;
@@ -1,4 +1,16 @@
1
+ /**
2
+ * Ensure password is a valid non-empty string
3
+ */
1
4
  export declare function ensureValidPassword(password: string): void;
5
+ /**
6
+ * Timing-safe comparison between two strings
7
+ */
2
8
  export declare function safeCompare(a: string, b: string): boolean;
9
+ /**
10
+ * Estimate password entropy based on character pool
11
+ */
3
12
  export declare function estimatePasswordEntropy(password: string): number;
13
+ /**
14
+ * Normalize password string to a consistent form
15
+ */
4
16
  export declare function normalizePassword(password: string): string;
@@ -9,11 +9,17 @@ exports.estimatePasswordEntropy = estimatePasswordEntropy;
9
9
  exports.normalizePassword = normalizePassword;
10
10
  const crypto_1 = __importDefault(require("crypto"));
11
11
  const errors_utils_1 = require("@naman_deep_singh/errors-utils");
12
+ /**
13
+ * Ensure password is a valid non-empty string
14
+ */
12
15
  function ensureValidPassword(password) {
13
16
  if (!password || typeof password !== 'string') {
14
- throw new errors_utils_1.BadRequestError('Invalid password provided');
17
+ throw new errors_utils_1.BadRequestError({ message: 'Invalid password provided' });
15
18
  }
16
19
  }
20
+ /**
21
+ * Timing-safe comparison between two strings
22
+ */
17
23
  function safeCompare(a, b) {
18
24
  const bufA = Buffer.from(a);
19
25
  const bufB = Buffer.from(b);
@@ -21,6 +27,9 @@ function safeCompare(a, b) {
21
27
  return false;
22
28
  return crypto_1.default.timingSafeEqual(bufA, bufB);
23
29
  }
30
+ /**
31
+ * Estimate password entropy based on character pool
32
+ */
24
33
  function estimatePasswordEntropy(password) {
25
34
  let pool = 0;
26
35
  if (/[a-z]/.test(password))
@@ -31,8 +40,14 @@ function estimatePasswordEntropy(password) {
31
40
  pool += 10;
32
41
  if (/[^A-Za-z0-9]/.test(password))
33
42
  pool += 32;
43
+ // If no characters matched, fallback to 1 to avoid log2(0)
44
+ if (pool === 0)
45
+ pool = 1;
34
46
  return password.length * Math.log2(pool);
35
47
  }
48
+ /**
49
+ * Normalize password string to a consistent form
50
+ */
36
51
  function normalizePassword(password) {
37
52
  return password.normalize('NFKC');
38
53
  }
@@ -15,11 +15,11 @@ const verifyPassword = async (password, hash) => {
15
15
  try {
16
16
  const result = await bcryptjs_1.default.compare(password, hash);
17
17
  if (!result)
18
- throw new errors_utils_1.UnauthorizedError('Password verification failed');
18
+ throw new errors_utils_1.UnauthorizedError({ message: 'Password verification failed' });
19
19
  return result;
20
20
  }
21
21
  catch {
22
- throw new errors_utils_1.UnauthorizedError('Password verification failed');
22
+ throw new errors_utils_1.UnauthorizedError({ message: 'Password verification failed' });
23
23
  }
24
24
  };
25
25
  exports.verifyPassword = verifyPassword;
@@ -33,11 +33,11 @@ const verifyPasswordSync = (password, hash) => {
33
33
  try {
34
34
  const result = bcryptjs_1.default.compareSync(password, hash);
35
35
  if (!result)
36
- throw new errors_utils_1.UnauthorizedError('Password verification failed');
36
+ throw new errors_utils_1.UnauthorizedError({ message: 'Password verification failed' });
37
37
  return result;
38
38
  }
39
39
  catch (_error) {
40
- throw new errors_utils_1.UnauthorizedError('Password verification failed');
40
+ throw new errors_utils_1.UnauthorizedError({ message: 'Password verification failed' });
41
41
  }
42
42
  };
43
43
  exports.verifyPasswordSync = verifyPasswordSync;
@@ -21,16 +21,11 @@ declare const _default: {
21
21
  generateTokens: (payload: Record<string, unknown>, accessSecret: import("node_modules/@types/jsonwebtoken").Secret, refreshSecret: import("node_modules/@types/jsonwebtoken").Secret, accessExpiry?: string | number, refreshExpiry?: string | number) => JWTUtils.TokenPair;
22
22
  parseDuration(input: string | number): number;
23
23
  signToken: (payload: Record<string, unknown>, secret: import("node_modules/@types/jsonwebtoken").Secret, expiresIn?: string | number, options?: import("node_modules/@types/jsonwebtoken").SignOptions) => string;
24
- validateTokenPayload(payload: Record<string, unknown>, rules?: JWTUtils.TokenRequirements): {
25
- valid: true;
26
- } | {
27
- valid: false;
28
- error: string;
29
- };
24
+ validateTokenPayload(payload: Record<string, unknown>, rules?: JWTUtils.TokenRequirements): void;
30
25
  isTokenExpired(payload: import("node_modules/@types/jsonwebtoken").JwtPayload): boolean;
31
26
  verifyToken: (token: string, secret: import("node_modules/@types/jsonwebtoken").Secret) => string | import("node_modules/@types/jsonwebtoken").JwtPayload;
32
- safeVerifyToken: (token: string, secret: import("node_modules/@types/jsonwebtoken").Secret) => JWTUtils.VerificationResult;
33
27
  verifyTokenWithOptions: (token: string, secret: import("node_modules/@types/jsonwebtoken").Secret, options?: import("node_modules/@types/jsonwebtoken").VerifyOptions) => string | import("node_modules/@types/jsonwebtoken").JwtPayload;
28
+ safeVerifyToken: (token: string, secret: import("node_modules/@types/jsonwebtoken").Secret) => JWTUtils.VerificationResult;
34
29
  safeVerifyTokenWithOptions: (token: string, secret: import("node_modules/@types/jsonwebtoken").Secret, options?: import("node_modules/@types/jsonwebtoken").VerifyOptions) => JWTUtils.VerificationResult;
35
30
  hashPasswordWithPepper(password: string, pepper: string): Promise<string>;
36
31
  hashPasswordWithPepperSync(password: string, pepper: string): string;