@naman_deep_singh/security 1.2.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.
- package/README.md +355 -176
- package/dist/cjs/core/crypto/cryptoManager.d.ts +111 -0
- package/dist/cjs/core/crypto/cryptoManager.js +191 -0
- package/dist/cjs/core/crypto/decrypt.js +6 -6
- package/dist/cjs/core/crypto/encrypt.js +4 -4
- package/dist/cjs/core/crypto/hmac.js +1 -1
- package/dist/cjs/core/crypto/index.d.ts +5 -4
- package/dist/cjs/core/crypto/index.js +12 -4
- package/dist/cjs/core/crypto/random.js +2 -2
- package/dist/cjs/core/jwt/decode.d.ts +1 -1
- package/dist/cjs/core/jwt/decode.js +2 -2
- package/dist/cjs/core/jwt/extractToken.js +7 -7
- package/dist/cjs/core/jwt/generateTokens.d.ts +2 -2
- package/dist/cjs/core/jwt/generateTokens.js +10 -6
- package/dist/cjs/core/jwt/index.d.ts +8 -8
- package/dist/cjs/core/jwt/jwtManager.d.ts +67 -0
- package/dist/cjs/core/jwt/jwtManager.js +299 -0
- package/dist/cjs/core/jwt/parseDuration.js +3 -3
- package/dist/cjs/core/jwt/signToken.d.ts +1 -1
- package/dist/cjs/core/jwt/signToken.js +7 -7
- package/dist/cjs/core/jwt/types.d.ts +1 -1
- package/dist/cjs/core/jwt/validateToken.d.ts +2 -2
- package/dist/cjs/core/jwt/validateToken.js +3 -3
- package/dist/cjs/core/jwt/verify.d.ts +3 -2
- package/dist/cjs/core/password/hash.js +1 -1
- package/dist/cjs/core/password/index.d.ts +3 -3
- package/dist/cjs/core/password/passwordManager.d.ts +29 -0
- package/dist/cjs/core/password/passwordManager.js +243 -0
- package/dist/cjs/core/password/strength.d.ts +1 -1
- package/dist/cjs/core/password/strength.js +4 -4
- package/dist/cjs/core/password/utils.js +2 -2
- package/dist/cjs/core/password/verify.js +1 -1
- package/dist/cjs/index.d.ts +9 -5
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/interfaces/jwt.interface.d.ts +47 -0
- package/dist/cjs/interfaces/jwt.interface.js +2 -0
- package/dist/cjs/interfaces/password.interface.d.ts +60 -0
- package/dist/cjs/interfaces/password.interface.js +2 -0
- package/dist/esm/core/crypto/cryptoManager.d.ts +111 -0
- package/dist/esm/core/crypto/cryptoManager.js +186 -0
- package/dist/esm/core/crypto/decrypt.js +7 -7
- package/dist/esm/core/crypto/encrypt.js +5 -5
- package/dist/esm/core/crypto/hmac.js +2 -2
- package/dist/esm/core/crypto/index.d.ts +5 -4
- package/dist/esm/core/crypto/index.js +5 -4
- package/dist/esm/core/crypto/random.js +3 -3
- package/dist/esm/core/jwt/decode.d.ts +1 -1
- package/dist/esm/core/jwt/decode.js +3 -3
- package/dist/esm/core/jwt/extractToken.js +7 -7
- package/dist/esm/core/jwt/generateTokens.d.ts +2 -2
- package/dist/esm/core/jwt/generateTokens.js +12 -8
- package/dist/esm/core/jwt/index.d.ts +8 -8
- package/dist/esm/core/jwt/index.js +8 -8
- package/dist/esm/core/jwt/jwtManager.d.ts +67 -0
- package/dist/esm/core/jwt/jwtManager.js +292 -0
- package/dist/esm/core/jwt/parseDuration.js +3 -3
- package/dist/esm/core/jwt/signToken.d.ts +1 -1
- package/dist/esm/core/jwt/signToken.js +9 -9
- package/dist/esm/core/jwt/types.d.ts +1 -1
- package/dist/esm/core/jwt/validateToken.d.ts +2 -2
- package/dist/esm/core/jwt/validateToken.js +3 -3
- package/dist/esm/core/jwt/verify.d.ts +3 -2
- package/dist/esm/core/jwt/verify.js +1 -1
- package/dist/esm/core/password/hash.js +3 -3
- package/dist/esm/core/password/index.d.ts +3 -3
- package/dist/esm/core/password/index.js +3 -3
- package/dist/esm/core/password/passwordManager.d.ts +29 -0
- package/dist/esm/core/password/passwordManager.js +236 -0
- package/dist/esm/core/password/strength.d.ts +1 -1
- package/dist/esm/core/password/strength.js +5 -5
- package/dist/esm/core/password/utils.js +4 -4
- package/dist/esm/core/password/verify.js +2 -2
- package/dist/esm/index.d.ts +9 -5
- package/dist/esm/index.js +7 -7
- package/dist/esm/interfaces/jwt.interface.d.ts +47 -0
- package/dist/esm/interfaces/jwt.interface.js +1 -0
- package/dist/esm/interfaces/password.interface.d.ts +60 -0
- package/dist/esm/interfaces/password.interface.js +1 -0
- package/dist/types/core/crypto/cryptoManager.d.ts +111 -0
- package/dist/types/core/crypto/index.d.ts +5 -4
- package/dist/types/core/jwt/decode.d.ts +1 -1
- package/dist/types/core/jwt/generateTokens.d.ts +2 -2
- package/dist/types/core/jwt/index.d.ts +8 -8
- package/dist/types/core/jwt/jwtManager.d.ts +67 -0
- package/dist/types/core/jwt/signToken.d.ts +1 -1
- package/dist/types/core/jwt/types.d.ts +1 -1
- package/dist/types/core/jwt/validateToken.d.ts +2 -2
- package/dist/types/core/jwt/verify.d.ts +3 -2
- package/dist/types/core/password/index.d.ts +3 -3
- package/dist/types/core/password/passwordManager.d.ts +29 -0
- package/dist/types/core/password/strength.d.ts +1 -1
- package/dist/types/index.d.ts +9 -5
- package/dist/types/interfaces/jwt.interface.d.ts +47 -0
- package/dist/types/interfaces/password.interface.d.ts +60 -0
- package/package.json +4 -3
|
@@ -0,0 +1,236 @@
|
|
|
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
|
+
export class PasswordManager {
|
|
6
|
+
constructor(config = {}) {
|
|
7
|
+
this.defaultConfig = {
|
|
8
|
+
saltRounds: 10,
|
|
9
|
+
minLength: 8,
|
|
10
|
+
maxLength: 128,
|
|
11
|
+
requireUppercase: true,
|
|
12
|
+
requireLowercase: true,
|
|
13
|
+
requireNumbers: true,
|
|
14
|
+
requireSpecialChars: false,
|
|
15
|
+
...config,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Hash a password asynchronously using bcrypt
|
|
20
|
+
*/
|
|
21
|
+
async hash(password, salt) {
|
|
22
|
+
try {
|
|
23
|
+
ensureValidPassword(password);
|
|
24
|
+
// Validate password meets basic requirements
|
|
25
|
+
this.validate(password);
|
|
26
|
+
const saltRounds = this.defaultConfig.saltRounds;
|
|
27
|
+
let passwordSalt = salt;
|
|
28
|
+
if (!passwordSalt) {
|
|
29
|
+
passwordSalt = await bcrypt.genSalt(saltRounds);
|
|
30
|
+
}
|
|
31
|
+
const hash = await bcrypt.hash(password, passwordSalt);
|
|
32
|
+
return {
|
|
33
|
+
hash,
|
|
34
|
+
salt: passwordSalt,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error instanceof BadRequestError ||
|
|
39
|
+
error instanceof ValidationError) {
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
throw new BadRequestError('Failed to hash password');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Verify password against hash and salt
|
|
47
|
+
*/
|
|
48
|
+
async verify(password, hash, salt) {
|
|
49
|
+
try {
|
|
50
|
+
if (!password || !hash || !salt) {
|
|
51
|
+
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;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Generate a random password
|
|
68
|
+
*/
|
|
69
|
+
generate(length = 16, options = {}) {
|
|
70
|
+
const config = { ...this.defaultConfig, ...options };
|
|
71
|
+
if (length < config.minLength || length > config.maxLength) {
|
|
72
|
+
throw new ValidationError(`Password length must be between ${config.minLength} and ${config.maxLength}`);
|
|
73
|
+
}
|
|
74
|
+
let charset = 'abcdefghijklmnopqrstuvwxyz';
|
|
75
|
+
if (config.requireUppercase)
|
|
76
|
+
charset += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
77
|
+
if (config.requireNumbers)
|
|
78
|
+
charset += '0123456789';
|
|
79
|
+
if (config.requireSpecialChars)
|
|
80
|
+
charset += '!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
81
|
+
let password = '';
|
|
82
|
+
const randomBytes = crypto.randomBytes(length);
|
|
83
|
+
for (let i = 0; i < length; i++) {
|
|
84
|
+
password += charset[randomBytes[i] % charset.length];
|
|
85
|
+
}
|
|
86
|
+
// Ensure all requirements are met
|
|
87
|
+
if (config.requireUppercase && !/[A-Z]/.test(password)) {
|
|
88
|
+
password = password.replace(/[a-z]/, 'A');
|
|
89
|
+
}
|
|
90
|
+
if (config.requireLowercase && !/[a-z]/.test(password)) {
|
|
91
|
+
password = password.replace(/[A-Z]/, 'a');
|
|
92
|
+
}
|
|
93
|
+
if (config.requireNumbers && !/[0-9]/.test(password)) {
|
|
94
|
+
password = password.replace(/[A-Za-z]/, '0');
|
|
95
|
+
}
|
|
96
|
+
if (config.requireSpecialChars && !/[^A-Za-z0-9]/.test(password)) {
|
|
97
|
+
password = password.replace(/[A-Za-z0-9]/, '!');
|
|
98
|
+
}
|
|
99
|
+
return password;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validate password against configuration
|
|
103
|
+
*/
|
|
104
|
+
validate(password, config = {}) {
|
|
105
|
+
const finalConfig = { ...this.defaultConfig, ...config };
|
|
106
|
+
const errors = [];
|
|
107
|
+
// Basic validation
|
|
108
|
+
if (!password || typeof password !== 'string') {
|
|
109
|
+
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) {
|
|
116
|
+
errors.push(`Password must not exceed ${finalConfig.maxLength} characters`);
|
|
117
|
+
}
|
|
118
|
+
// Complexity requirements
|
|
119
|
+
if (finalConfig.requireUppercase && !/[A-Z]/.test(password)) {
|
|
120
|
+
errors.push('Password must contain at least one uppercase letter');
|
|
121
|
+
}
|
|
122
|
+
if (finalConfig.requireLowercase && !/[a-z]/.test(password)) {
|
|
123
|
+
errors.push('Password must contain at least one lowercase letter');
|
|
124
|
+
}
|
|
125
|
+
if (finalConfig.requireNumbers && !/[0-9]/.test(password)) {
|
|
126
|
+
errors.push('Password must contain at least one number');
|
|
127
|
+
}
|
|
128
|
+
if (finalConfig.requireSpecialChars && !/[^A-Za-z0-9]/.test(password)) {
|
|
129
|
+
errors.push('Password must contain at least one special character');
|
|
130
|
+
}
|
|
131
|
+
// Custom rules
|
|
132
|
+
if (finalConfig.customRules) {
|
|
133
|
+
finalConfig.customRules.forEach((rule) => {
|
|
134
|
+
if (!rule.test(password)) {
|
|
135
|
+
errors.push(rule.message);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
const strength = this.checkStrength(password);
|
|
140
|
+
const isValid = errors.length === 0;
|
|
141
|
+
return {
|
|
142
|
+
isValid,
|
|
143
|
+
errors,
|
|
144
|
+
strength,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Check password strength
|
|
149
|
+
*/
|
|
150
|
+
checkStrength(password) {
|
|
151
|
+
const entropy = estimatePasswordEntropy(password);
|
|
152
|
+
let score = 0;
|
|
153
|
+
const feedback = [];
|
|
154
|
+
const suggestions = [];
|
|
155
|
+
// Length scoring
|
|
156
|
+
if (password.length >= 8)
|
|
157
|
+
score++;
|
|
158
|
+
if (password.length >= 12)
|
|
159
|
+
score++;
|
|
160
|
+
if (password.length >= 16)
|
|
161
|
+
score++;
|
|
162
|
+
// Character variety scoring
|
|
163
|
+
if (/[a-z]/.test(password))
|
|
164
|
+
score++;
|
|
165
|
+
if (/[A-Z]/.test(password))
|
|
166
|
+
score++;
|
|
167
|
+
if (/[0-9]/.test(password))
|
|
168
|
+
score++;
|
|
169
|
+
if (/[^A-Za-z0-9]/.test(password))
|
|
170
|
+
score++;
|
|
171
|
+
// Common patterns deduction
|
|
172
|
+
if (/^[A-Za-z]+$/.test(password)) {
|
|
173
|
+
score--;
|
|
174
|
+
feedback.push('Consider adding numbers and symbols');
|
|
175
|
+
}
|
|
176
|
+
if (/^[0-9]+$/.test(password)) {
|
|
177
|
+
score -= 2;
|
|
178
|
+
feedback.push('Avoid using only numbers');
|
|
179
|
+
}
|
|
180
|
+
if (/([a-zA-Z0-9])\1{2,}/.test(password)) {
|
|
181
|
+
score--;
|
|
182
|
+
feedback.push('Avoid repeated characters');
|
|
183
|
+
}
|
|
184
|
+
if (/(?:012|123|234|345|456|567|678|789)/.test(password)) {
|
|
185
|
+
score--;
|
|
186
|
+
feedback.push('Avoid sequential patterns');
|
|
187
|
+
}
|
|
188
|
+
// Common passwords check
|
|
189
|
+
const commonPasswords = ['password', '123456', 'qwerty', 'admin', 'letmein'];
|
|
190
|
+
if (commonPasswords.some((common) => password.toLowerCase().includes(common))) {
|
|
191
|
+
score = 0;
|
|
192
|
+
feedback.push('Avoid common passwords');
|
|
193
|
+
}
|
|
194
|
+
// Clamp score and determine label
|
|
195
|
+
score = Math.max(0, Math.min(4, score));
|
|
196
|
+
let label;
|
|
197
|
+
switch (score) {
|
|
198
|
+
case 0:
|
|
199
|
+
label = 'very-weak';
|
|
200
|
+
suggestions.push('Use a longer password with mixed characters');
|
|
201
|
+
break;
|
|
202
|
+
case 1:
|
|
203
|
+
label = 'weak';
|
|
204
|
+
suggestions.push('Add more character variety');
|
|
205
|
+
break;
|
|
206
|
+
case 2:
|
|
207
|
+
label = 'fair';
|
|
208
|
+
suggestions.push('Consider adding more length or character types');
|
|
209
|
+
break;
|
|
210
|
+
case 3:
|
|
211
|
+
label = 'good';
|
|
212
|
+
suggestions.push('Your password is reasonably secure');
|
|
213
|
+
break;
|
|
214
|
+
case 4:
|
|
215
|
+
label = 'strong';
|
|
216
|
+
suggestions.push('Your password is very secure');
|
|
217
|
+
break;
|
|
218
|
+
default:
|
|
219
|
+
label = 'very-weak';
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
score,
|
|
223
|
+
label,
|
|
224
|
+
feedback,
|
|
225
|
+
suggestions,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Check if password hash needs upgrade (different salt rounds)
|
|
230
|
+
*/
|
|
231
|
+
needsUpgrade(hash, currentConfig) {
|
|
232
|
+
// Simple heuristic: if the hash doesn't match current salt rounds pattern
|
|
233
|
+
// In practice, you'd need to store the salt rounds with the hash
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { PasswordStrengthOptions } from
|
|
1
|
+
import type { PasswordStrengthOptions } from './types';
|
|
2
2
|
export declare const isPasswordStrong: (password: string, options?: PasswordStrengthOptions) => boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BadRequestError, ValidationError } from
|
|
1
|
+
import { BadRequestError, ValidationError, } from '@naman_deep_singh/errors-utils';
|
|
2
2
|
export const isPasswordStrong = (password, options = {}) => {
|
|
3
3
|
if (!password)
|
|
4
4
|
throw new BadRequestError('Invalid password provided');
|
|
@@ -6,12 +6,12 @@ export const isPasswordStrong = (password, 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(
|
|
9
|
+
throw new ValidationError('Password must include uppercase letters');
|
|
10
10
|
if (requireLowercase && !/[a-z]/.test(password))
|
|
11
|
-
throw new ValidationError(
|
|
11
|
+
throw new ValidationError('Password must include lowercase letters');
|
|
12
12
|
if (requireNumbers && !/[0-9]/.test(password))
|
|
13
|
-
throw new ValidationError(
|
|
13
|
+
throw new ValidationError('Password must include numbers');
|
|
14
14
|
if (requireSymbols && !/[^A-Za-z0-9]/.test(password))
|
|
15
|
-
throw new ValidationError(
|
|
15
|
+
throw new ValidationError('Password must include symbols');
|
|
16
16
|
return true;
|
|
17
17
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import crypto from
|
|
2
|
-
import { BadRequestError } from
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { BadRequestError } from '@naman_deep_singh/errors-utils';
|
|
3
3
|
export function ensureValidPassword(password) {
|
|
4
|
-
if (!password || typeof password !==
|
|
4
|
+
if (!password || typeof password !== 'string') {
|
|
5
5
|
throw new BadRequestError('Invalid password provided');
|
|
6
6
|
}
|
|
7
7
|
}
|
|
@@ -25,5 +25,5 @@ export function estimatePasswordEntropy(password) {
|
|
|
25
25
|
return password.length * Math.log2(pool);
|
|
26
26
|
}
|
|
27
27
|
export function normalizePassword(password) {
|
|
28
|
-
return password.normalize(
|
|
28
|
+
return password.normalize('NFKC');
|
|
29
29
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { UnauthorizedError } from '@naman_deep_singh/errors-utils';
|
|
2
|
+
import bcrypt from 'bcryptjs';
|
|
3
3
|
/**
|
|
4
4
|
* Compare a password with a stored hash asynchronously.
|
|
5
5
|
*/
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export { BadRequestError, UnauthorizedError, ValidationError, InternalServerError } from
|
|
5
|
-
import * as
|
|
1
|
+
export * from './core/password';
|
|
2
|
+
export * from './core/jwt';
|
|
3
|
+
export * from './core/crypto';
|
|
4
|
+
export { BadRequestError, UnauthorizedError, ValidationError, InternalServerError, } from '@naman_deep_singh/errors-utils';
|
|
5
|
+
import * as CryptoUtils from './core/crypto';
|
|
6
|
+
import * as JWTUtils from './core/jwt';
|
|
6
7
|
declare const _default: {
|
|
7
8
|
decrypt: (data: string, secret: string) => string;
|
|
8
9
|
encrypt: (text: string, secret: string) => string;
|
|
@@ -10,6 +11,9 @@ declare const _default: {
|
|
|
10
11
|
hmacVerify: (message: string, secret: string, signature: string) => boolean;
|
|
11
12
|
randomToken: (length?: number) => string;
|
|
12
13
|
generateStrongPassword: (length?: number) => string;
|
|
14
|
+
CryptoManager: typeof CryptoUtils.CryptoManager;
|
|
15
|
+
createCryptoManager: (config?: CryptoUtils.CryptoManagerConfig) => CryptoUtils.CryptoManager;
|
|
16
|
+
cryptoManager: CryptoUtils.CryptoManager;
|
|
13
17
|
decodeToken(token: string): null | string | import("node_modules/@types/jsonwebtoken").JwtPayload;
|
|
14
18
|
decodeTokenStrict(token: string): import("node_modules/@types/jsonwebtoken").JwtPayload;
|
|
15
19
|
extractToken(sources: JWTUtils.TokenSources): string | null;
|
package/dist/esm/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
1
|
+
export * from './core/password';
|
|
2
|
+
export * from './core/jwt';
|
|
3
|
+
export * from './core/crypto';
|
|
4
4
|
// Re-export common errors for convenience
|
|
5
|
-
export { BadRequestError, UnauthorizedError, ValidationError, InternalServerError } from
|
|
6
|
-
import * as
|
|
7
|
-
import * as JWTUtils from
|
|
8
|
-
import * as
|
|
5
|
+
export { BadRequestError, UnauthorizedError, ValidationError, InternalServerError, } from '@naman_deep_singh/errors-utils';
|
|
6
|
+
import * as CryptoUtils from './core/crypto';
|
|
7
|
+
import * as JWTUtils from './core/jwt';
|
|
8
|
+
import * as PasswordUtils from './core/password';
|
|
9
9
|
export default {
|
|
10
10
|
...PasswordUtils,
|
|
11
11
|
...JWTUtils,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { JwtPayload, Secret } from 'jsonwebtoken';
|
|
2
|
+
export interface AccessToken extends String {
|
|
3
|
+
readonly __type: 'AccessToken';
|
|
4
|
+
}
|
|
5
|
+
export interface RefreshToken extends String {
|
|
6
|
+
readonly __type: 'RefreshToken';
|
|
7
|
+
}
|
|
8
|
+
export interface TokenPair {
|
|
9
|
+
accessToken: AccessToken;
|
|
10
|
+
refreshToken: RefreshToken;
|
|
11
|
+
}
|
|
12
|
+
export interface JWTConfig {
|
|
13
|
+
accessSecret: Secret;
|
|
14
|
+
refreshSecret: Secret;
|
|
15
|
+
accessExpiry?: string | number;
|
|
16
|
+
refreshExpiry?: string | number;
|
|
17
|
+
enableCaching?: boolean;
|
|
18
|
+
maxCacheSize?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface TokenValidationOptions {
|
|
21
|
+
ignoreExpiration?: boolean;
|
|
22
|
+
ignoreNotBefore?: boolean;
|
|
23
|
+
audience?: string | string[];
|
|
24
|
+
issuer?: string;
|
|
25
|
+
algorithms?: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface TokenGenerationOptions {
|
|
28
|
+
algorithm?: string;
|
|
29
|
+
expiresIn?: string | number;
|
|
30
|
+
audience?: string | string[];
|
|
31
|
+
issuer?: string;
|
|
32
|
+
subject?: string;
|
|
33
|
+
kid?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ITokenManager {
|
|
36
|
+
generateTokens(payload: Record<string, unknown>): Promise<TokenPair>;
|
|
37
|
+
generateAccessToken(payload: Record<string, unknown>): Promise<AccessToken>;
|
|
38
|
+
generateRefreshToken(payload: Record<string, unknown>): Promise<RefreshToken>;
|
|
39
|
+
verifyAccessToken(token: string): Promise<JwtPayload | string>;
|
|
40
|
+
verifyRefreshToken(token: string): Promise<JwtPayload | string>;
|
|
41
|
+
decodeToken(token: string, complete?: boolean): JwtPayload | string | null;
|
|
42
|
+
extractTokenFromHeader(authHeader: string): string | null;
|
|
43
|
+
validateToken(token: string, secret: Secret, options?: TokenValidationOptions): boolean;
|
|
44
|
+
rotateRefreshToken(oldToken: string): Promise<RefreshToken>;
|
|
45
|
+
isTokenExpired(token: string): boolean;
|
|
46
|
+
getTokenExpiration(token: string): Date | null;
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export interface PasswordConfig {
|
|
2
|
+
saltRounds?: number;
|
|
3
|
+
minLength?: number;
|
|
4
|
+
maxLength?: number;
|
|
5
|
+
requireUppercase?: boolean;
|
|
6
|
+
requireLowercase?: boolean;
|
|
7
|
+
requireNumbers?: boolean;
|
|
8
|
+
requireSpecialChars?: boolean;
|
|
9
|
+
customRules?: PasswordRule[];
|
|
10
|
+
}
|
|
11
|
+
export interface PasswordRule {
|
|
12
|
+
test: (password: string) => boolean;
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
export interface PasswordStrength {
|
|
16
|
+
score: number;
|
|
17
|
+
label: 'very-weak' | 'weak' | 'fair' | 'good' | 'strong';
|
|
18
|
+
feedback: string[];
|
|
19
|
+
suggestions: string[];
|
|
20
|
+
}
|
|
21
|
+
export interface PasswordValidationResult {
|
|
22
|
+
isValid: boolean;
|
|
23
|
+
errors: string[];
|
|
24
|
+
strength: PasswordStrength;
|
|
25
|
+
}
|
|
26
|
+
export interface HashedPassword {
|
|
27
|
+
hash: string;
|
|
28
|
+
salt: string;
|
|
29
|
+
}
|
|
30
|
+
export interface IPasswordManager {
|
|
31
|
+
hash(password: string, salt?: string): Promise<HashedPassword>;
|
|
32
|
+
verify(password: string, hash: string, salt: string): Promise<boolean>;
|
|
33
|
+
generate(length?: number, options?: PasswordConfig): string;
|
|
34
|
+
validate(password: string, config?: PasswordConfig): PasswordValidationResult;
|
|
35
|
+
checkStrength(password: string): PasswordStrength;
|
|
36
|
+
needsUpgrade(hash: string, currentConfig: PasswordConfig): boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface IPasswordStrengthChecker {
|
|
39
|
+
analyze(password: string): PasswordStrength;
|
|
40
|
+
checkLength(password: string): {
|
|
41
|
+
valid: boolean;
|
|
42
|
+
message: string;
|
|
43
|
+
};
|
|
44
|
+
checkComplexity(password: string, config: PasswordConfig): {
|
|
45
|
+
valid: boolean;
|
|
46
|
+
message: string;
|
|
47
|
+
}[];
|
|
48
|
+
checkCommonPasswords(password: string): {
|
|
49
|
+
valid: boolean;
|
|
50
|
+
message: string;
|
|
51
|
+
};
|
|
52
|
+
checkSequential(password: string): {
|
|
53
|
+
valid: boolean;
|
|
54
|
+
message: string;
|
|
55
|
+
};
|
|
56
|
+
checkRepetition(password: string): {
|
|
57
|
+
valid: boolean;
|
|
58
|
+
message: string;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for CryptoManager
|
|
3
|
+
*/
|
|
4
|
+
export interface CryptoManagerConfig {
|
|
5
|
+
defaultAlgorithm?: string;
|
|
6
|
+
defaultEncoding?: BufferEncoding;
|
|
7
|
+
hmacAlgorithm?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* CryptoManager - Class-based wrapper for all cryptographic operations
|
|
11
|
+
* Provides a consistent interface for encryption, decryption, HMAC generation, and secure random generation
|
|
12
|
+
*/
|
|
13
|
+
export declare class CryptoManager {
|
|
14
|
+
private config;
|
|
15
|
+
constructor(config?: CryptoManagerConfig);
|
|
16
|
+
/**
|
|
17
|
+
* Update configuration
|
|
18
|
+
*/
|
|
19
|
+
updateConfig(config: Partial<CryptoManagerConfig>): void;
|
|
20
|
+
/**
|
|
21
|
+
* Get current configuration
|
|
22
|
+
*/
|
|
23
|
+
getConfig(): Required<CryptoManagerConfig>;
|
|
24
|
+
/**
|
|
25
|
+
* Encrypt data using the default or specified algorithm
|
|
26
|
+
*/
|
|
27
|
+
encrypt(plaintext: string, key: string, options?: {
|
|
28
|
+
algorithm?: string;
|
|
29
|
+
encoding?: BufferEncoding;
|
|
30
|
+
iv?: string;
|
|
31
|
+
}): string;
|
|
32
|
+
/**
|
|
33
|
+
* Decrypt data using the default or specified algorithm
|
|
34
|
+
*/
|
|
35
|
+
decrypt(encryptedData: string, key: string, options?: {
|
|
36
|
+
algorithm?: string;
|
|
37
|
+
encoding?: BufferEncoding;
|
|
38
|
+
iv?: string;
|
|
39
|
+
}): string;
|
|
40
|
+
/**
|
|
41
|
+
* Generate HMAC signature
|
|
42
|
+
*/
|
|
43
|
+
generateHmac(data: string, secret: string, options?: {
|
|
44
|
+
algorithm?: string;
|
|
45
|
+
encoding?: BufferEncoding;
|
|
46
|
+
}): string;
|
|
47
|
+
/**
|
|
48
|
+
* Generate cryptographically secure random bytes
|
|
49
|
+
*/
|
|
50
|
+
generateSecureRandom(length: number, encoding?: BufferEncoding): string;
|
|
51
|
+
/**
|
|
52
|
+
* Verify HMAC signature
|
|
53
|
+
*/
|
|
54
|
+
verifyHmac(data: string, secret: string, signature: string, options?: {
|
|
55
|
+
algorithm?: string;
|
|
56
|
+
encoding?: BufferEncoding;
|
|
57
|
+
}): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Create a key derivation function using PBKDF2
|
|
60
|
+
*/
|
|
61
|
+
deriveKey(password: string, salt: string, iterations?: number, keyLength?: number): Promise<string>;
|
|
62
|
+
/**
|
|
63
|
+
* Hash data using SHA-256
|
|
64
|
+
*/
|
|
65
|
+
sha256(data: string, encoding?: BufferEncoding): string;
|
|
66
|
+
/**
|
|
67
|
+
* Hash data using SHA-512
|
|
68
|
+
*/
|
|
69
|
+
sha512(data: string, encoding?: BufferEncoding): string;
|
|
70
|
+
/**
|
|
71
|
+
* Generate a secure key pair for asymmetric encryption
|
|
72
|
+
*/
|
|
73
|
+
generateKeyPair(options?: {
|
|
74
|
+
modulusLength?: number;
|
|
75
|
+
publicKeyEncoding?: {
|
|
76
|
+
type: string;
|
|
77
|
+
format: string;
|
|
78
|
+
};
|
|
79
|
+
privateKeyEncoding?: {
|
|
80
|
+
type: string;
|
|
81
|
+
format: string;
|
|
82
|
+
};
|
|
83
|
+
}): Promise<{
|
|
84
|
+
publicKey: string;
|
|
85
|
+
privateKey: string;
|
|
86
|
+
}>;
|
|
87
|
+
/**
|
|
88
|
+
* Encrypt data using RSA public key
|
|
89
|
+
*/
|
|
90
|
+
rsaEncrypt(data: string, publicKey: string): Promise<string>;
|
|
91
|
+
/**
|
|
92
|
+
* Decrypt data using RSA private key
|
|
93
|
+
*/
|
|
94
|
+
rsaDecrypt(encryptedData: string, privateKey: string): Promise<string>;
|
|
95
|
+
/**
|
|
96
|
+
* Create digital signature using RSA private key
|
|
97
|
+
*/
|
|
98
|
+
rsaSign(data: string, privateKey: string, algorithm?: string): Promise<string>;
|
|
99
|
+
/**
|
|
100
|
+
* Verify digital signature using RSA public key
|
|
101
|
+
*/
|
|
102
|
+
rsaVerify(data: string, signature: string, publicKey: string, algorithm?: string): Promise<boolean>;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Create a CryptoManager instance with default configuration
|
|
106
|
+
*/
|
|
107
|
+
export declare const createCryptoManager: (config?: CryptoManagerConfig) => CryptoManager;
|
|
108
|
+
/**
|
|
109
|
+
* Default CryptoManager instance
|
|
110
|
+
*/
|
|
111
|
+
export declare const cryptoManager: CryptoManager;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
1
|
+
export { decrypt } from './decrypt';
|
|
2
|
+
export { encrypt } from './encrypt';
|
|
3
|
+
export { hmacSign, hmacVerify } from './hmac';
|
|
4
|
+
export { randomToken, generateStrongPassword } from './random';
|
|
5
|
+
export * from './cryptoManager';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Secret } from
|
|
2
|
-
import { RefreshToken, TokenPair } from
|
|
1
|
+
import { type Secret } from 'jsonwebtoken';
|
|
2
|
+
import type { RefreshToken, TokenPair } from './types';
|
|
3
3
|
export declare const generateTokens: (payload: Record<string, unknown>, accessSecret: Secret, refreshSecret: Secret, accessExpiry?: string | number, refreshExpiry?: string | number) => TokenPair;
|
|
4
4
|
export declare function rotateRefreshToken(oldToken: string, secret: Secret): RefreshToken;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
1
|
+
export * from './decode';
|
|
2
|
+
export * from './extractToken';
|
|
3
|
+
export * from './generateTokens';
|
|
4
|
+
export * from './parseDuration';
|
|
5
|
+
export * from './signToken';
|
|
6
|
+
export * from './types';
|
|
7
|
+
export * from './validateToken';
|
|
8
|
+
export * from './verify';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type JwtPayload, type Secret } from 'jsonwebtoken';
|
|
2
|
+
import type { AccessToken, ITokenManager, JWTConfig, RefreshToken, TokenPair, TokenValidationOptions } from '../../interfaces/jwt.interface';
|
|
3
|
+
export declare class JWTManager implements ITokenManager {
|
|
4
|
+
private accessSecret;
|
|
5
|
+
private refreshSecret;
|
|
6
|
+
private accessExpiry;
|
|
7
|
+
private refreshExpiry;
|
|
8
|
+
private cache?;
|
|
9
|
+
private cacheTTL;
|
|
10
|
+
constructor(config: JWTConfig);
|
|
11
|
+
/**
|
|
12
|
+
* Generate both access and refresh tokens
|
|
13
|
+
*/
|
|
14
|
+
generateTokens(payload: Record<string, unknown>): Promise<TokenPair>;
|
|
15
|
+
/**
|
|
16
|
+
* Generate access token
|
|
17
|
+
*/
|
|
18
|
+
generateAccessToken(payload: Record<string, unknown>): Promise<AccessToken>;
|
|
19
|
+
/**
|
|
20
|
+
* Generate refresh token
|
|
21
|
+
*/
|
|
22
|
+
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
|
+
*/
|
|
34
|
+
decodeToken(token: string, complete?: boolean): JwtPayload | string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Extract token from Authorization header
|
|
37
|
+
*/
|
|
38
|
+
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
|
+
*/
|
|
46
|
+
rotateRefreshToken(oldToken: string): Promise<RefreshToken>;
|
|
47
|
+
/**
|
|
48
|
+
* Check if token is expired
|
|
49
|
+
*/
|
|
50
|
+
isTokenExpired(token: string): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Get token expiration date
|
|
53
|
+
*/
|
|
54
|
+
getTokenExpiration(token: string): Date | null;
|
|
55
|
+
/**
|
|
56
|
+
* Clear token cache
|
|
57
|
+
*/
|
|
58
|
+
clearCache(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Get cache statistics
|
|
61
|
+
*/
|
|
62
|
+
getCacheStats(): {
|
|
63
|
+
size: number;
|
|
64
|
+
maxSize: number;
|
|
65
|
+
} | null;
|
|
66
|
+
private validatePayload;
|
|
67
|
+
}
|