@naman_deep_singh/security 1.3.0 → 1.3.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.
Files changed (84) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/core/crypto/cryptoManager.js +9 -3
  3. package/dist/cjs/core/crypto/decrypt.js +6 -6
  4. package/dist/cjs/core/crypto/encrypt.js +4 -4
  5. package/dist/cjs/core/crypto/hmac.js +1 -1
  6. package/dist/cjs/core/crypto/index.d.ts +5 -5
  7. package/dist/cjs/core/crypto/random.js +2 -2
  8. package/dist/cjs/core/jwt/decode.d.ts +1 -1
  9. package/dist/cjs/core/jwt/decode.js +2 -2
  10. package/dist/cjs/core/jwt/extractToken.js +7 -7
  11. package/dist/cjs/core/jwt/generateTokens.d.ts +2 -2
  12. package/dist/cjs/core/jwt/generateTokens.js +10 -6
  13. package/dist/cjs/core/jwt/index.d.ts +8 -8
  14. package/dist/cjs/core/jwt/jwtManager.d.ts +3 -2
  15. package/dist/cjs/core/jwt/jwtManager.js +66 -86
  16. package/dist/cjs/core/jwt/parseDuration.js +3 -3
  17. package/dist/cjs/core/jwt/signToken.d.ts +1 -1
  18. package/dist/cjs/core/jwt/signToken.js +7 -7
  19. package/dist/cjs/core/jwt/types.d.ts +1 -1
  20. package/dist/cjs/core/jwt/validateToken.d.ts +2 -2
  21. package/dist/cjs/core/jwt/validateToken.js +3 -3
  22. package/dist/cjs/core/jwt/verify.d.ts +3 -2
  23. package/dist/cjs/core/password/hash.js +1 -1
  24. package/dist/cjs/core/password/index.d.ts +3 -3
  25. package/dist/cjs/core/password/passwordManager.d.ts +1 -1
  26. package/dist/cjs/core/password/passwordManager.js +32 -31
  27. package/dist/cjs/core/password/strength.d.ts +1 -1
  28. package/dist/cjs/core/password/strength.js +4 -4
  29. package/dist/cjs/core/password/utils.js +2 -2
  30. package/dist/cjs/core/password/verify.js +1 -1
  31. package/dist/cjs/index.d.ts +6 -6
  32. package/dist/cjs/index.js +2 -2
  33. package/dist/cjs/interfaces/jwt.interface.d.ts +1 -1
  34. package/dist/esm/core/crypto/cryptoManager.js +10 -4
  35. package/dist/esm/core/crypto/decrypt.js +7 -7
  36. package/dist/esm/core/crypto/encrypt.js +5 -5
  37. package/dist/esm/core/crypto/hmac.js +2 -2
  38. package/dist/esm/core/crypto/index.d.ts +5 -5
  39. package/dist/esm/core/crypto/index.js +5 -5
  40. package/dist/esm/core/crypto/random.js +3 -3
  41. package/dist/esm/core/jwt/decode.d.ts +1 -1
  42. package/dist/esm/core/jwt/decode.js +3 -3
  43. package/dist/esm/core/jwt/extractToken.js +7 -7
  44. package/dist/esm/core/jwt/generateTokens.d.ts +2 -2
  45. package/dist/esm/core/jwt/generateTokens.js +12 -8
  46. package/dist/esm/core/jwt/index.d.ts +8 -8
  47. package/dist/esm/core/jwt/index.js +8 -8
  48. package/dist/esm/core/jwt/jwtManager.d.ts +3 -2
  49. package/dist/esm/core/jwt/jwtManager.js +70 -90
  50. package/dist/esm/core/jwt/parseDuration.js +3 -3
  51. package/dist/esm/core/jwt/signToken.d.ts +1 -1
  52. package/dist/esm/core/jwt/signToken.js +9 -9
  53. package/dist/esm/core/jwt/types.d.ts +1 -1
  54. package/dist/esm/core/jwt/validateToken.d.ts +2 -2
  55. package/dist/esm/core/jwt/validateToken.js +3 -3
  56. package/dist/esm/core/jwt/verify.d.ts +3 -2
  57. package/dist/esm/core/jwt/verify.js +1 -1
  58. package/dist/esm/core/password/hash.js +3 -3
  59. package/dist/esm/core/password/index.d.ts +3 -3
  60. package/dist/esm/core/password/index.js +3 -3
  61. package/dist/esm/core/password/passwordManager.d.ts +1 -1
  62. package/dist/esm/core/password/passwordManager.js +34 -33
  63. package/dist/esm/core/password/strength.d.ts +1 -1
  64. package/dist/esm/core/password/strength.js +5 -5
  65. package/dist/esm/core/password/utils.js +4 -4
  66. package/dist/esm/core/password/verify.js +2 -2
  67. package/dist/esm/index.d.ts +6 -6
  68. package/dist/esm/index.js +7 -7
  69. package/dist/esm/interfaces/jwt.interface.d.ts +1 -1
  70. package/dist/types/core/crypto/index.d.ts +5 -5
  71. package/dist/types/core/jwt/decode.d.ts +1 -1
  72. package/dist/types/core/jwt/generateTokens.d.ts +2 -2
  73. package/dist/types/core/jwt/index.d.ts +8 -8
  74. package/dist/types/core/jwt/jwtManager.d.ts +3 -2
  75. package/dist/types/core/jwt/signToken.d.ts +1 -1
  76. package/dist/types/core/jwt/types.d.ts +1 -1
  77. package/dist/types/core/jwt/validateToken.d.ts +2 -2
  78. package/dist/types/core/jwt/verify.d.ts +3 -2
  79. package/dist/types/core/password/index.d.ts +3 -3
  80. package/dist/types/core/password/passwordManager.d.ts +1 -1
  81. package/dist/types/core/password/strength.d.ts +1 -1
  82. package/dist/types/index.d.ts +6 -6
  83. package/dist/types/interfaces/jwt.interface.d.ts +1 -1
  84. package/package.json +4 -3
@@ -1,52 +1,17 @@
1
- import jwt from "jsonwebtoken";
2
- import { signToken } from "./signToken";
3
- import { verifyToken, safeVerifyToken } from "./verify";
4
- import { BadRequestError, UnauthorizedError, ValidationError } from "@naman_deep_singh/errors-utils";
5
- // Simple LRU cache for token validation
6
- class TokenCache {
7
- constructor(maxSize = 100, ttl = 5 * 60 * 1000) {
8
- this.cache = new Map();
9
- this.maxSize = maxSize;
10
- this.ttl = ttl;
11
- }
12
- get(key) {
13
- const entry = this.cache.get(key);
14
- if (!entry)
15
- return null;
16
- if (Date.now() - entry.timestamp > this.ttl) {
17
- this.cache.delete(key);
18
- return null;
19
- }
20
- // Move to end (most recently used)
21
- this.cache.delete(key);
22
- this.cache.set(key, entry);
23
- return { valid: entry.valid, payload: entry.payload };
24
- }
25
- set(key, value) {
26
- if (this.cache.has(key)) {
27
- this.cache.delete(key);
28
- }
29
- else if (this.cache.size >= this.maxSize) {
30
- // Remove least recently used (first item)
31
- const firstKey = this.cache.keys().next().value;
32
- if (firstKey !== undefined) {
33
- this.cache.delete(firstKey);
34
- }
35
- }
36
- this.cache.set(key, { ...value, timestamp: Date.now() });
37
- }
38
- clear() {
39
- this.cache.clear();
40
- }
41
- }
1
+ import jwt from 'jsonwebtoken';
2
+ import { signToken } from './signToken';
3
+ import { safeVerifyToken, verifyToken } from './verify';
4
+ import { BadRequestError, UnauthorizedError, ValidationError, } from '@naman_deep_singh/errors-utils';
5
+ import { LRUCache } from '@naman_deep_singh/js-extensions';
42
6
  export class JWTManager {
43
7
  constructor(config) {
44
8
  this.accessSecret = config.accessSecret;
45
9
  this.refreshSecret = config.refreshSecret;
46
- this.accessExpiry = config.accessExpiry || "15m";
47
- this.refreshExpiry = config.refreshExpiry || "7d";
10
+ this.accessExpiry = config.accessExpiry || '15m';
11
+ this.refreshExpiry = config.refreshExpiry || '7d';
12
+ this.cacheTTL = 5 * 60 * 1000; // 5 minutes default TTL
48
13
  if (config.enableCaching) {
49
- this.cache = new TokenCache(config.maxCacheSize || 100);
14
+ this.cache = new LRUCache(config.maxCacheSize || 100);
50
15
  }
51
16
  }
52
17
  /**
@@ -63,10 +28,11 @@ export class JWTManager {
63
28
  };
64
29
  }
65
30
  catch (error) {
66
- if (error instanceof BadRequestError || error instanceof ValidationError) {
31
+ if (error instanceof BadRequestError ||
32
+ error instanceof ValidationError) {
67
33
  throw error;
68
34
  }
69
- throw new BadRequestError("Failed to generate tokens");
35
+ throw new BadRequestError('Failed to generate tokens');
70
36
  }
71
37
  }
72
38
  /**
@@ -75,14 +41,17 @@ export class JWTManager {
75
41
  async generateAccessToken(payload) {
76
42
  try {
77
43
  this.validatePayload(payload);
78
- const token = signToken(payload, this.accessSecret, this.accessExpiry, { algorithm: "HS256" });
44
+ const token = signToken(payload, this.accessSecret, this.accessExpiry, {
45
+ algorithm: 'HS256',
46
+ });
79
47
  return token;
80
48
  }
81
49
  catch (error) {
82
- if (error instanceof BadRequestError || error instanceof ValidationError) {
50
+ if (error instanceof BadRequestError ||
51
+ error instanceof ValidationError) {
83
52
  throw error;
84
53
  }
85
- throw new BadRequestError("Failed to generate access token");
54
+ throw new BadRequestError('Failed to generate access token');
86
55
  }
87
56
  }
88
57
  /**
@@ -91,14 +60,17 @@ export class JWTManager {
91
60
  async generateRefreshToken(payload) {
92
61
  try {
93
62
  this.validatePayload(payload);
94
- const token = signToken(payload, this.refreshSecret, this.refreshExpiry, { algorithm: "HS256" });
63
+ const token = signToken(payload, this.refreshSecret, this.refreshExpiry, {
64
+ algorithm: 'HS256',
65
+ });
95
66
  return token;
96
67
  }
97
68
  catch (error) {
98
- if (error instanceof BadRequestError || error instanceof ValidationError) {
69
+ if (error instanceof BadRequestError ||
70
+ error instanceof ValidationError) {
99
71
  throw error;
100
72
  }
101
- throw new BadRequestError("Failed to generate refresh token");
73
+ throw new BadRequestError('Failed to generate refresh token');
102
74
  }
103
75
  }
104
76
  /**
@@ -106,36 +78,41 @@ export class JWTManager {
106
78
  */
107
79
  async verifyAccessToken(token) {
108
80
  try {
109
- if (!token || typeof token !== "string") {
110
- throw new ValidationError("Access token must be a non-empty string");
81
+ if (!token || typeof token !== 'string') {
82
+ throw new ValidationError('Access token must be a non-empty string');
111
83
  }
112
84
  const cacheKey = `access_${token}`;
113
85
  if (this.cache) {
114
86
  const cached = this.cache.get(cacheKey);
115
- if (cached) {
87
+ if (cached && Date.now() - cached.timestamp <= this.cacheTTL) {
116
88
  if (!cached.valid) {
117
- throw new UnauthorizedError("Access token is invalid or expired");
89
+ throw new UnauthorizedError('Access token is invalid or expired');
118
90
  }
119
91
  return cached.payload;
120
92
  }
121
93
  }
122
94
  const decoded = verifyToken(token, this.accessSecret);
123
95
  if (this.cache) {
124
- this.cache.set(cacheKey, { valid: true, payload: decoded });
96
+ this.cache.set(cacheKey, {
97
+ valid: true,
98
+ payload: decoded,
99
+ timestamp: Date.now(),
100
+ });
125
101
  }
126
102
  return decoded;
127
103
  }
128
104
  catch (error) {
129
- if (error instanceof ValidationError || error instanceof UnauthorizedError) {
105
+ if (error instanceof ValidationError ||
106
+ error instanceof UnauthorizedError) {
130
107
  throw error;
131
108
  }
132
- if (error instanceof Error && error.name === "TokenExpiredError") {
133
- throw new UnauthorizedError("Access token has expired");
109
+ if (error instanceof Error && error.name === 'TokenExpiredError') {
110
+ throw new UnauthorizedError('Access token has expired');
134
111
  }
135
- if (error instanceof Error && error.name === "JsonWebTokenError") {
136
- throw new UnauthorizedError("Access token is invalid");
112
+ if (error instanceof Error && error.name === 'JsonWebTokenError') {
113
+ throw new UnauthorizedError('Access token is invalid');
137
114
  }
138
- throw new UnauthorizedError("Failed to verify access token");
115
+ throw new UnauthorizedError('Failed to verify access token');
139
116
  }
140
117
  }
141
118
  /**
@@ -143,36 +120,37 @@ export class JWTManager {
143
120
  */
144
121
  async verifyRefreshToken(token) {
145
122
  try {
146
- if (!token || typeof token !== "string") {
147
- throw new ValidationError("Refresh token must be a non-empty string");
123
+ if (!token || typeof token !== 'string') {
124
+ throw new ValidationError('Refresh token must be a non-empty string');
148
125
  }
149
126
  const cacheKey = `refresh_${token}`;
150
127
  if (this.cache) {
151
128
  const cached = this.cache.get(cacheKey);
152
129
  if (cached) {
153
130
  if (!cached.valid) {
154
- throw new UnauthorizedError("Refresh token is invalid or expired");
131
+ throw new UnauthorizedError('Refresh token is invalid or expired');
155
132
  }
156
133
  return cached.payload;
157
134
  }
158
135
  }
159
136
  const decoded = verifyToken(token, this.refreshSecret);
160
137
  if (this.cache) {
161
- this.cache.set(cacheKey, { valid: true, payload: decoded });
138
+ this.cache.set(cacheKey, { valid: true, payload: decoded, timestamp: Date.now() });
162
139
  }
163
140
  return decoded;
164
141
  }
165
142
  catch (error) {
166
- if (error instanceof ValidationError || error instanceof UnauthorizedError) {
143
+ if (error instanceof ValidationError ||
144
+ error instanceof UnauthorizedError) {
167
145
  throw error;
168
146
  }
169
- if (error instanceof Error && error.name === "TokenExpiredError") {
170
- throw new UnauthorizedError("Refresh token has expired");
147
+ if (error instanceof Error && error.name === 'TokenExpiredError') {
148
+ throw new UnauthorizedError('Refresh token has expired');
171
149
  }
172
- if (error instanceof Error && error.name === "JsonWebTokenError") {
173
- throw new UnauthorizedError("Refresh token is invalid");
150
+ if (error instanceof Error && error.name === 'JsonWebTokenError') {
151
+ throw new UnauthorizedError('Refresh token is invalid');
174
152
  }
175
- throw new UnauthorizedError("Failed to verify refresh token");
153
+ throw new UnauthorizedError('Failed to verify refresh token');
176
154
  }
177
155
  }
178
156
  /**
@@ -180,8 +158,8 @@ export class JWTManager {
180
158
  */
181
159
  decodeToken(token, complete = false) {
182
160
  try {
183
- if (!token || typeof token !== "string") {
184
- throw new ValidationError("Token must be a non-empty string");
161
+ if (!token || typeof token !== 'string') {
162
+ throw new ValidationError('Token must be a non-empty string');
185
163
  }
186
164
  return jwt.decode(token, { complete });
187
165
  }
@@ -197,11 +175,11 @@ export class JWTManager {
197
175
  */
198
176
  extractTokenFromHeader(authHeader) {
199
177
  try {
200
- if (!authHeader || typeof authHeader !== "string") {
178
+ if (!authHeader || typeof authHeader !== 'string') {
201
179
  return null;
202
180
  }
203
- const parts = authHeader.split(" ");
204
- if (parts.length !== 2 || parts[0] !== "Bearer") {
181
+ const parts = authHeader.split(' ');
182
+ if (parts.length !== 2 || parts[0] !== 'Bearer') {
205
183
  return null;
206
184
  }
207
185
  return parts[1];
@@ -215,7 +193,7 @@ export class JWTManager {
215
193
  */
216
194
  validateToken(token, secret, options = {}) {
217
195
  try {
218
- if (!token || typeof token !== "string") {
196
+ if (!token || typeof token !== 'string') {
219
197
  return false;
220
198
  }
221
199
  const result = safeVerifyToken(token, secret);
@@ -230,12 +208,12 @@ export class JWTManager {
230
208
  */
231
209
  async rotateRefreshToken(oldToken) {
232
210
  try {
233
- if (!oldToken || typeof oldToken !== "string") {
234
- throw new ValidationError("Old refresh token must be a non-empty string");
211
+ if (!oldToken || typeof oldToken !== 'string') {
212
+ throw new ValidationError('Old refresh token must be a non-empty string');
235
213
  }
236
214
  const decoded = await this.verifyRefreshToken(oldToken);
237
- if (typeof decoded === "string") {
238
- throw new ValidationError("Invalid token payload — expected JWT payload object");
215
+ if (typeof decoded === 'string') {
216
+ throw new ValidationError('Invalid token payload — expected JWT payload object');
239
217
  }
240
218
  // Create new payload without issued/expired timestamps
241
219
  const payload = { ...decoded };
@@ -246,10 +224,11 @@ export class JWTManager {
246
224
  return newToken;
247
225
  }
248
226
  catch (error) {
249
- if (error instanceof ValidationError || error instanceof UnauthorizedError) {
227
+ if (error instanceof ValidationError ||
228
+ error instanceof UnauthorizedError) {
250
229
  throw error;
251
230
  }
252
- throw new BadRequestError("Failed to rotate refresh token");
231
+ throw new BadRequestError('Failed to rotate refresh token');
253
232
  }
254
233
  }
255
234
  /**
@@ -295,18 +274,19 @@ export class JWTManager {
295
274
  getCacheStats() {
296
275
  if (!this.cache)
297
276
  return null;
277
+ // Note: LRUCache doesn't expose internal size, so we return maxSize only
298
278
  return {
299
- size: this.cache.cache.size,
300
- maxSize: this.cache.maxSize
279
+ size: -1, // Size not available from LRUCache
280
+ maxSize: this.cache.maxSize,
301
281
  };
302
282
  }
303
283
  // Private helper methods
304
284
  validatePayload(payload) {
305
- if (!payload || typeof payload !== "object") {
306
- throw new ValidationError("Payload must be a non-null object");
285
+ if (!payload || typeof payload !== 'object') {
286
+ throw new ValidationError('Payload must be a non-null object');
307
287
  }
308
288
  if (Object.keys(payload).length === 0) {
309
- throw new ValidationError("Payload cannot be empty");
289
+ throw new ValidationError('Payload cannot be empty');
310
290
  }
311
291
  }
312
292
  }
@@ -3,16 +3,16 @@ const TIME_UNITS = {
3
3
  m: 60,
4
4
  h: 3600,
5
5
  d: 86400,
6
- w: 604800
6
+ w: 604800,
7
7
  };
8
8
  export function parseDuration(input) {
9
- if (typeof input === "number")
9
+ if (typeof input === 'number')
10
10
  return input;
11
11
  const regex = /(\d+)\s*(s|m|h|d|w)/gi;
12
12
  let totalSeconds = 0;
13
13
  let match;
14
14
  while ((match = regex.exec(input)) !== null) {
15
- const value = parseInt(match[1], 10);
15
+ const value = Number.parseInt(match[1], 10);
16
16
  const unit = match[2].toLowerCase();
17
17
  if (!TIME_UNITS[unit]) {
18
18
  throw new Error(`Invalid time unit: ${unit}`);
@@ -1,2 +1,2 @@
1
- import { Secret, SignOptions } from "jsonwebtoken";
1
+ import { type Secret, type SignOptions } from 'jsonwebtoken';
2
2
  export declare const signToken: (payload: Record<string, unknown>, secret: Secret, expiresIn?: string | number, options?: SignOptions) => string;
@@ -1,22 +1,22 @@
1
- import { sign } from "jsonwebtoken";
2
- import { parseDuration } from "./parseDuration";
1
+ import { sign } from 'jsonwebtoken';
2
+ import { parseDuration } from './parseDuration';
3
3
  function getExpiryTimestamp(seconds) {
4
4
  return Math.floor(Date.now() / 1000) + seconds;
5
5
  }
6
- export const signToken = (payload, secret, expiresIn = "1h", options = {}) => {
6
+ export const signToken = (payload, secret, expiresIn = '1h', options = {}) => {
7
7
  const seconds = parseDuration(expiresIn);
8
8
  if (!seconds || seconds < 10) {
9
- throw new Error("Token expiry too small");
9
+ throw new Error('Token expiry too small');
10
10
  }
11
11
  const tokenPayload = {
12
- ...payload
12
+ ...payload,
13
13
  };
14
- if (!("exp" in payload))
14
+ if (!('exp' in payload))
15
15
  tokenPayload.exp = getExpiryTimestamp(seconds);
16
- if (!("iat" in payload))
16
+ if (!('iat' in payload))
17
17
  tokenPayload.iat = Math.floor(Date.now() / 1000);
18
18
  return sign(tokenPayload, secret, {
19
- algorithm: "HS256",
20
- ...options
19
+ algorithm: 'HS256',
20
+ ...options,
21
21
  });
22
22
  };
@@ -1,4 +1,4 @@
1
- import { JwtPayload } from "jsonwebtoken";
1
+ import type { JwtPayload } from 'jsonwebtoken';
2
2
  export interface AccessTokenBrand {
3
3
  readonly access: unique symbol;
4
4
  }
@@ -1,8 +1,8 @@
1
- import { JwtPayload } from "node_modules/@types/jsonwebtoken";
1
+ import type { JwtPayload } from 'node_modules/@types/jsonwebtoken';
2
2
  export interface TokenRequirements {
3
3
  requiredFields?: string[];
4
4
  forbiddenFields?: string[];
5
- validateTypes?: Record<string, "string" | "number" | "boolean">;
5
+ validateTypes?: Record<string, 'string' | 'number' | 'boolean'>;
6
6
  }
7
7
  export declare function validateTokenPayload(payload: Record<string, unknown>, rules?: TokenRequirements): {
8
8
  valid: true;
@@ -1,7 +1,7 @@
1
1
  export function validateTokenPayload(payload, rules = {
2
- requiredFields: ["exp", "iat"]
2
+ requiredFields: ['exp', 'iat'],
3
3
  }) {
4
- const { requiredFields = [], forbiddenFields = [], validateTypes = {} } = rules;
4
+ const { requiredFields = [], forbiddenFields = [], validateTypes = {}, } = rules;
5
5
  // 1. Required fields
6
6
  for (const field of requiredFields) {
7
7
  if (!(field in payload)) {
@@ -20,7 +20,7 @@ export function validateTokenPayload(payload, rules = {
20
20
  if (key in payload && typeof payload[key] !== expectedType) {
21
21
  return {
22
22
  valid: false,
23
- error: `Invalid type for ${key}. Expected ${expectedType}.`
23
+ error: `Invalid type for ${key}. Expected ${expectedType}.`,
24
24
  };
25
25
  }
26
26
  }
@@ -1,5 +1,6 @@
1
- import jwt, { Secret, JwtPayload } from "jsonwebtoken";
2
- import { VerificationResult } from "./types";
1
+ import type jwt from 'jsonwebtoken';
2
+ import { type JwtPayload, type Secret } from 'jsonwebtoken';
3
+ import type { VerificationResult } from './types';
3
4
  /**
4
5
  * Verify token (throws if invalid or expired)
5
6
  */
@@ -1,4 +1,4 @@
1
- import { verify } from "jsonwebtoken";
1
+ import { verify } from 'jsonwebtoken';
2
2
  /**
3
3
  * Verify token (throws if invalid or expired)
4
4
  */
@@ -1,6 +1,6 @@
1
- import bcrypt from "bcryptjs";
2
- import { ensureValidPassword } from "./utils";
3
- import { InternalServerError } from "@naman_deep_singh/errors-utils";
1
+ import { InternalServerError } from '@naman_deep_singh/errors-utils';
2
+ import bcrypt from 'bcryptjs';
3
+ import { ensureValidPassword } from './utils';
4
4
  /**
5
5
  * Hash a password asynchronously using bcrypt.
6
6
  */
@@ -1,3 +1,3 @@
1
- export * from "./hash";
2
- export * from "./strength";
3
- export * from "./verify";
1
+ export * from './hash';
2
+ export * from './strength';
3
+ export * from './verify';
@@ -1,3 +1,3 @@
1
- export * from "./hash";
2
- export * from "./strength";
3
- export * from "./verify";
1
+ export * from './hash';
2
+ export * from './strength';
3
+ export * from './verify';
@@ -1,4 +1,4 @@
1
- import { IPasswordManager, PasswordConfig, PasswordValidationResult, HashedPassword, PasswordStrength } from "../../interfaces/password.interface";
1
+ import type { HashedPassword, IPasswordManager, PasswordConfig, PasswordStrength, PasswordValidationResult } from '../../interfaces/password.interface';
2
2
  export declare class PasswordManager implements IPasswordManager {
3
3
  private defaultConfig;
4
4
  constructor(config?: PasswordConfig);
@@ -1,7 +1,7 @@
1
- import bcrypt from "bcryptjs";
2
- import crypto from "crypto";
3
- import { ensureValidPassword, estimatePasswordEntropy } from "./utils";
4
- import { BadRequestError, ValidationError } from "@naman_deep_singh/errors-utils";
1
+ import crypto from 'crypto';
2
+ import bcrypt from 'bcryptjs';
3
+ import { BadRequestError, ValidationError, } from '@naman_deep_singh/errors-utils';
4
+ import { ensureValidPassword, estimatePasswordEntropy } from './utils';
5
5
  export class PasswordManager {
6
6
  constructor(config = {}) {
7
7
  this.defaultConfig = {
@@ -12,7 +12,7 @@ export class PasswordManager {
12
12
  requireLowercase: true,
13
13
  requireNumbers: true,
14
14
  requireSpecialChars: false,
15
- ...config
15
+ ...config,
16
16
  };
17
17
  }
18
18
  /**
@@ -31,14 +31,15 @@ export class PasswordManager {
31
31
  const hash = await bcrypt.hash(password, passwordSalt);
32
32
  return {
33
33
  hash,
34
- salt: passwordSalt
34
+ salt: passwordSalt,
35
35
  };
36
36
  }
37
37
  catch (error) {
38
- if (error instanceof BadRequestError || error instanceof ValidationError) {
38
+ if (error instanceof BadRequestError ||
39
+ error instanceof ValidationError) {
39
40
  throw error;
40
41
  }
41
- throw new BadRequestError("Failed to hash password");
42
+ throw new BadRequestError('Failed to hash password');
42
43
  }
43
44
  }
44
45
  /**
@@ -70,14 +71,14 @@ export class PasswordManager {
70
71
  if (length < config.minLength || length > config.maxLength) {
71
72
  throw new ValidationError(`Password length must be between ${config.minLength} and ${config.maxLength}`);
72
73
  }
73
- let charset = "abcdefghijklmnopqrstuvwxyz";
74
+ let charset = 'abcdefghijklmnopqrstuvwxyz';
74
75
  if (config.requireUppercase)
75
- charset += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
76
+ charset += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
76
77
  if (config.requireNumbers)
77
- charset += "0123456789";
78
+ charset += '0123456789';
78
79
  if (config.requireSpecialChars)
79
- charset += "!@#$%^&*()_+-=[]{}|;:,.<>?";
80
- let password = "";
80
+ charset += '!@#$%^&*()_+-=[]{}|;:,.<>?';
81
+ let password = '';
81
82
  const randomBytes = crypto.randomBytes(length);
82
83
  for (let i = 0; i < length; i++) {
83
84
  password += charset[randomBytes[i] % charset.length];
@@ -104,8 +105,8 @@ export class PasswordManager {
104
105
  const finalConfig = { ...this.defaultConfig, ...config };
105
106
  const errors = [];
106
107
  // Basic validation
107
- if (!password || typeof password !== "string") {
108
- errors.push("Password must be a non-empty string");
108
+ if (!password || typeof password !== 'string') {
109
+ errors.push('Password must be a non-empty string');
109
110
  }
110
111
  // Length validation
111
112
  if (password.length < finalConfig.minLength) {
@@ -116,20 +117,20 @@ export class PasswordManager {
116
117
  }
117
118
  // Complexity requirements
118
119
  if (finalConfig.requireUppercase && !/[A-Z]/.test(password)) {
119
- errors.push("Password must contain at least one uppercase letter");
120
+ errors.push('Password must contain at least one uppercase letter');
120
121
  }
121
122
  if (finalConfig.requireLowercase && !/[a-z]/.test(password)) {
122
- errors.push("Password must contain at least one lowercase letter");
123
+ errors.push('Password must contain at least one lowercase letter');
123
124
  }
124
125
  if (finalConfig.requireNumbers && !/[0-9]/.test(password)) {
125
- errors.push("Password must contain at least one number");
126
+ errors.push('Password must contain at least one number');
126
127
  }
127
128
  if (finalConfig.requireSpecialChars && !/[^A-Za-z0-9]/.test(password)) {
128
- errors.push("Password must contain at least one special character");
129
+ errors.push('Password must contain at least one special character');
129
130
  }
130
131
  // Custom rules
131
132
  if (finalConfig.customRules) {
132
- finalConfig.customRules.forEach(rule => {
133
+ finalConfig.customRules.forEach((rule) => {
133
134
  if (!rule.test(password)) {
134
135
  errors.push(rule.message);
135
136
  }
@@ -140,7 +141,7 @@ export class PasswordManager {
140
141
  return {
141
142
  isValid,
142
143
  errors,
143
- strength
144
+ strength,
144
145
  };
145
146
  }
146
147
  /**
@@ -170,25 +171,25 @@ export class PasswordManager {
170
171
  // Common patterns deduction
171
172
  if (/^[A-Za-z]+$/.test(password)) {
172
173
  score--;
173
- feedback.push("Consider adding numbers and symbols");
174
+ feedback.push('Consider adding numbers and symbols');
174
175
  }
175
176
  if (/^[0-9]+$/.test(password)) {
176
177
  score -= 2;
177
- feedback.push("Avoid using only numbers");
178
+ feedback.push('Avoid using only numbers');
178
179
  }
179
180
  if (/([a-zA-Z0-9])\1{2,}/.test(password)) {
180
181
  score--;
181
- feedback.push("Avoid repeated characters");
182
+ feedback.push('Avoid repeated characters');
182
183
  }
183
184
  if (/(?:012|123|234|345|456|567|678|789)/.test(password)) {
184
185
  score--;
185
- feedback.push("Avoid sequential patterns");
186
+ feedback.push('Avoid sequential patterns');
186
187
  }
187
188
  // Common passwords check
188
189
  const commonPasswords = ['password', '123456', 'qwerty', 'admin', 'letmein'];
189
- if (commonPasswords.some(common => password.toLowerCase().includes(common))) {
190
+ if (commonPasswords.some((common) => password.toLowerCase().includes(common))) {
190
191
  score = 0;
191
- feedback.push("Avoid common passwords");
192
+ feedback.push('Avoid common passwords');
192
193
  }
193
194
  // Clamp score and determine label
194
195
  score = Math.max(0, Math.min(4, score));
@@ -196,23 +197,23 @@ export class PasswordManager {
196
197
  switch (score) {
197
198
  case 0:
198
199
  label = 'very-weak';
199
- suggestions.push("Use a longer password with mixed characters");
200
+ suggestions.push('Use a longer password with mixed characters');
200
201
  break;
201
202
  case 1:
202
203
  label = 'weak';
203
- suggestions.push("Add more character variety");
204
+ suggestions.push('Add more character variety');
204
205
  break;
205
206
  case 2:
206
207
  label = 'fair';
207
- suggestions.push("Consider adding more length or character types");
208
+ suggestions.push('Consider adding more length or character types');
208
209
  break;
209
210
  case 3:
210
211
  label = 'good';
211
- suggestions.push("Your password is reasonably secure");
212
+ suggestions.push('Your password is reasonably secure');
212
213
  break;
213
214
  case 4:
214
215
  label = 'strong';
215
- suggestions.push("Your password is very secure");
216
+ suggestions.push('Your password is very secure');
216
217
  break;
217
218
  default:
218
219
  label = 'very-weak';
@@ -221,7 +222,7 @@ export class PasswordManager {
221
222
  score,
222
223
  label,
223
224
  feedback,
224
- suggestions
225
+ suggestions,
225
226
  };
226
227
  }
227
228
  /**
@@ -1,2 +1,2 @@
1
- import { PasswordStrengthOptions } from "./types";
1
+ import type { PasswordStrengthOptions } from './types';
2
2
  export declare const isPasswordStrong: (password: string, options?: PasswordStrengthOptions) => boolean;