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