@naman_deep_singh/security 1.2.0 โ 1.3.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.
- package/README.md +355 -176
- package/dist/cjs/core/crypto/cryptoManager.d.ts +111 -0
- package/dist/cjs/core/crypto/cryptoManager.js +185 -0
- package/dist/cjs/core/crypto/index.d.ts +5 -4
- package/dist/cjs/core/crypto/index.js +12 -4
- package/dist/cjs/core/jwt/jwtManager.d.ts +66 -0
- package/dist/cjs/core/jwt/jwtManager.js +319 -0
- package/dist/cjs/core/password/passwordManager.d.ts +29 -0
- package/dist/cjs/core/password/passwordManager.js +242 -0
- package/dist/cjs/index.d.ts +4 -0
- 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 +180 -0
- package/dist/esm/core/crypto/index.d.ts +5 -4
- package/dist/esm/core/crypto/index.js +5 -4
- package/dist/esm/core/jwt/jwtManager.d.ts +66 -0
- package/dist/esm/core/jwt/jwtManager.js +312 -0
- package/dist/esm/core/password/passwordManager.d.ts +29 -0
- package/dist/esm/core/password/passwordManager.js +235 -0
- package/dist/esm/index.d.ts +4 -0
- 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/jwtManager.d.ts +66 -0
- package/dist/types/core/password/passwordManager.d.ts +29 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/interfaces/jwt.interface.d.ts +47 -0
- package/dist/types/interfaces/password.interface.d.ts +60 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,109 +1,222 @@
|
|
|
1
1
|
# @naman_deep_singh/security
|
|
2
2
|
|
|
3
|
-
**Version:** 1.
|
|
3
|
+
**Version:** 1.3.0
|
|
4
4
|
|
|
5
5
|
A complete, lightweight security toolkit for Node.js & TypeScript providing:
|
|
6
6
|
|
|
7
|
-
๐ **Password hashing & validation** with bcrypt
|
|
8
|
-
๐ **JWT signing & verification** (no deprecated expiresIn)
|
|
7
|
+
๐ **Password hashing & validation** with bcrypt (async/sync, peppered variants)
|
|
8
|
+
๐ **JWT signing & verification** (no deprecated expiresIn, with caching)
|
|
9
9
|
๐งฎ **Duration parser** ("15m", "7d", etc.)
|
|
10
|
-
๐ชช **Token generator** (access + refresh pair)
|
|
10
|
+
๐ชช **Token generator** (access + refresh pair with branded types)
|
|
11
11
|
โป๏ธ **Refresh token rotation** helper
|
|
12
12
|
๐งฐ **Robust token extraction** (Headers, Cookies, Query, Body, WebSocket)
|
|
13
13
|
๐งฉ **Safe & strict JWT decode** utilities
|
|
14
|
+
๐ **AES-256-GCM encryption/decryption** with HMAC and random utilities
|
|
14
15
|
๐จ **Standardized error handling** with @naman_deep_singh/errors-utils
|
|
15
16
|
|
|
16
17
|
โ **Fully typed** with TypeScript
|
|
17
18
|
โ **Branded token types** for compile-time safety (AccessToken/RefreshToken)
|
|
18
|
-
โ **
|
|
19
|
-
โ **
|
|
19
|
+
โ **Class-based managers** for advanced features (PasswordManager, JWTManager, CryptoManager)
|
|
20
|
+
โ **Functional exports** for simple use cases
|
|
21
|
+
โ **Password strength checking** and validation
|
|
22
|
+
โ **Token caching** for performance
|
|
20
23
|
โ **Consistent errors** across your application ecosystem
|
|
21
24
|
โ **Works in both ESM and CommonJS**
|
|
22
25
|
|
|
23
26
|
```bash
|
|
24
|
-
|
|
25
27
|
๐ฆ Installation
|
|
26
28
|
npm install @naman_deep_singh/security
|
|
29
|
+
```
|
|
27
30
|
|
|
28
31
|
๐ง Features
|
|
29
32
|
|
|
30
33
|
๐ฅ Password Hashing โ secure & async (bcrypt with 10 salt rounds)
|
|
34
|
+
๐ฅ Password Validation & Strength Checking
|
|
35
|
+
๐ฅ Password Generation with configurable requirements
|
|
36
|
+
๐ฅ Peppered hashing variants
|
|
37
|
+
๐ฅ Synchronous & asynchronous versions
|
|
31
38
|
|
|
32
39
|
๐ฅ Custom Expiry JWT โ manual exp support using duration strings
|
|
33
|
-
|
|
34
40
|
๐ฅ Token Pair Generation (accessToken + refreshToken)
|
|
35
|
-
|
|
36
41
|
๐ฅ Refresh Token Rotation
|
|
37
|
-
|
|
38
42
|
๐ฅ Safe & Unsafe JWT Verification
|
|
39
|
-
|
|
40
43
|
๐ฅ Strict vs Flexible Decoding
|
|
41
|
-
|
|
42
44
|
๐ฅ Universal Token Extraction (Headers, Cookies, Query, Body, WebSocket)
|
|
45
|
+
๐ฅ Token caching for performance
|
|
43
46
|
|
|
44
|
-
๐ฅ
|
|
47
|
+
๐ฅ AES-256-GCM Encryption/Decryption
|
|
48
|
+
๐ฅ HMAC signing and verification
|
|
49
|
+
๐ฅ Cryptographically secure random generation
|
|
45
50
|
|
|
46
|
-
๐ฅ Production-grade types
|
|
51
|
+
๐ฅ Production-grade types and error handling
|
|
47
52
|
|
|
48
53
|
๐ Quick Start
|
|
54
|
+
|
|
55
|
+
### Functional Approach (Simple)
|
|
56
|
+
```typescript
|
|
49
57
|
import {
|
|
50
58
|
hashPassword,
|
|
51
59
|
verifyPassword,
|
|
52
60
|
generateTokens,
|
|
53
61
|
verifyToken,
|
|
54
62
|
safeVerifyToken,
|
|
55
|
-
extractToken
|
|
63
|
+
extractToken,
|
|
64
|
+
encrypt,
|
|
65
|
+
decrypt
|
|
56
66
|
} from "@naman_deep_singh/security";
|
|
57
67
|
|
|
68
|
+
// Password operations
|
|
69
|
+
const hashed = await hashPassword("mypassword");
|
|
70
|
+
const isValid = await verifyPassword("mypassword", hashed);
|
|
71
|
+
|
|
72
|
+
// JWT operations
|
|
73
|
+
const tokens = generateTokens(
|
|
74
|
+
{ userId: 42 },
|
|
75
|
+
process.env.ACCESS_SECRET!,
|
|
76
|
+
process.env.REFRESH_SECRET!,
|
|
77
|
+
"15m",
|
|
78
|
+
"7d"
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const result = safeVerifyToken(tokens.accessToken, process.env.ACCESS_SECRET!);
|
|
82
|
+
|
|
83
|
+
// Crypto operations
|
|
84
|
+
const encrypted = encrypt("sensitive data", "secret-key");
|
|
85
|
+
const decrypted = decrypt(encrypted, "secret-key");
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Class-Based Approach (Advanced)
|
|
89
|
+
```typescript
|
|
90
|
+
import { PasswordManager, JWTManager, CryptoManager } from "@naman_deep_singh/security";
|
|
91
|
+
|
|
92
|
+
// Password Manager with validation
|
|
93
|
+
const passwordManager = new PasswordManager({
|
|
94
|
+
minLength: 12,
|
|
95
|
+
requireUppercase: true,
|
|
96
|
+
requireNumbers: true,
|
|
97
|
+
requireSpecialChars: true
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const validation = passwordManager.validate("MySecurePass123!");
|
|
101
|
+
if (validation.isValid) {
|
|
102
|
+
const hashed = await passwordManager.hash("MySecurePass123!");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// JWT Manager with caching
|
|
106
|
+
const jwtManager = new JWTManager({
|
|
107
|
+
accessSecret: process.env.ACCESS_SECRET!,
|
|
108
|
+
refreshSecret: process.env.REFRESH_SECRET!,
|
|
109
|
+
accessExpiry: "15m",
|
|
110
|
+
refreshExpiry: "7d",
|
|
111
|
+
enableCaching: true
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const tokens = await jwtManager.generateTokens({ userId: 42 });
|
|
115
|
+
const payload = await jwtManager.verifyAccessToken(tokens.accessToken);
|
|
116
|
+
|
|
117
|
+
// Crypto Manager
|
|
118
|
+
const cryptoManager = new CryptoManager("your-secret-key");
|
|
119
|
+
const encrypted = cryptoManager.encrypt("data");
|
|
120
|
+
const decrypted = cryptoManager.decrypt(encrypted);
|
|
121
|
+
```
|
|
122
|
+
|
|
58
123
|
๐ API Documentation
|
|
59
124
|
|
|
60
125
|
Below is a complete reference with full usage examples.
|
|
61
126
|
|
|
62
|
-
๐ง 1. Password Utilities
|
|
63
|
-
hashPassword(password: string): Promise<string>
|
|
64
|
-
const hashed = await hashPassword("mypassword");
|
|
65
|
-
console.log(hashed); // $2a$10$...
|
|
127
|
+
## ๐ง 1. Password Utilities
|
|
66
128
|
|
|
129
|
+
### Functional Exports
|
|
130
|
+
```typescript
|
|
131
|
+
// Async hashing
|
|
132
|
+
const hashed = await hashPassword("mypassword"); // Uses 10 salt rounds by default
|
|
133
|
+
const hashed = await hashPassword("mypassword", 12); // Custom salt rounds
|
|
67
134
|
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
135
|
+
// Sync hashing
|
|
136
|
+
const hashedSync = hashPasswordSync("mypassword");
|
|
137
|
+
const hashedSync = hashPasswordSync("mypassword", 12);
|
|
138
|
+
|
|
139
|
+
// Peppered variants
|
|
140
|
+
const hashedPeppered = await hashPasswordWithPepper("mypassword", "pepper");
|
|
141
|
+
const hashedPepperedSync = hashPasswordWithPepperSync("mypassword", "pepper");
|
|
71
142
|
|
|
72
|
-
//
|
|
143
|
+
// Verification
|
|
144
|
+
const isValid = await verifyPassword("mypassword", hashed);
|
|
73
145
|
const isValidSync = verifyPasswordSync("mypassword", hashed);
|
|
74
146
|
|
|
75
|
-
|
|
76
|
-
|
|
147
|
+
// Peppered verification
|
|
148
|
+
const isValidPeppered = await verifyPasswordWithPepper("mypassword", "pepper", hashed);
|
|
149
|
+
const isValidPepperedSync = verifyPasswordWithPepperSync("mypassword", "pepper", hashed);
|
|
150
|
+
```
|
|
77
151
|
|
|
78
|
-
|
|
152
|
+
### PasswordManager Class
|
|
153
|
+
```typescript
|
|
154
|
+
const passwordManager = new PasswordManager({
|
|
155
|
+
saltRounds: 12,
|
|
156
|
+
minLength: 8,
|
|
157
|
+
maxLength: 128,
|
|
158
|
+
requireUppercase: true,
|
|
159
|
+
requireLowercase: true,
|
|
160
|
+
requireNumbers: true,
|
|
161
|
+
requireSpecialChars: false,
|
|
162
|
+
customRules: [
|
|
163
|
+
{ test: (pwd) => !pwd.includes('password'), message: 'Cannot contain "password"' }
|
|
164
|
+
]
|
|
165
|
+
});
|
|
79
166
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"1h"
|
|
84
|
-
);
|
|
167
|
+
// Hash with validation
|
|
168
|
+
const result = await passwordManager.hash("MySecurePass123!");
|
|
169
|
+
// Returns: { hash: "$2a$...", salt: "..." }
|
|
85
170
|
|
|
86
|
-
|
|
171
|
+
// Verify
|
|
172
|
+
const isValid = await passwordManager.verify("MySecurePass123!", result.hash, result.salt);
|
|
87
173
|
|
|
174
|
+
// Validate password
|
|
175
|
+
const validation = passwordManager.validate("MySecurePass123!");
|
|
176
|
+
/*
|
|
177
|
+
Returns: {
|
|
178
|
+
isValid: true,
|
|
179
|
+
errors: [],
|
|
180
|
+
strength: { score: 4, label: 'strong', feedback: [...], suggestions: [...] }
|
|
181
|
+
}
|
|
182
|
+
*/
|
|
88
183
|
|
|
89
|
-
|
|
90
|
-
|
|
184
|
+
// Generate secure password
|
|
185
|
+
const generatedPassword = passwordManager.generate(16, {
|
|
186
|
+
requireUppercase: true,
|
|
187
|
+
requireNumbers: true,
|
|
188
|
+
requireSpecialChars: true
|
|
189
|
+
});
|
|
91
190
|
|
|
92
|
-
|
|
191
|
+
// Check strength
|
|
192
|
+
const strength = passwordManager.checkStrength("MySecurePass123!");
|
|
193
|
+
/*
|
|
194
|
+
Returns: {
|
|
195
|
+
score: 4,
|
|
196
|
+
label: 'strong',
|
|
197
|
+
feedback: [],
|
|
198
|
+
suggestions: ["Your password is very secure"]
|
|
199
|
+
}
|
|
200
|
+
*/
|
|
201
|
+
```
|
|
93
202
|
|
|
94
|
-
|
|
203
|
+
## ๐ 2. JWT Utilities
|
|
204
|
+
|
|
205
|
+
### Functional Exports
|
|
206
|
+
```typescript
|
|
207
|
+
// Sign token with duration string
|
|
208
|
+
const token = signToken(
|
|
209
|
+
{ userId: 1, role: "admin" },
|
|
210
|
+
process.env.JWT_SECRET!,
|
|
211
|
+
"1h" // Duration string: "15m", "2h", "7d", "30s"
|
|
212
|
+
);
|
|
95
213
|
|
|
214
|
+
// Parse duration to seconds
|
|
96
215
|
parseDuration("15m"); // 900
|
|
97
216
|
parseDuration("2h"); // 7200
|
|
98
217
|
parseDuration("7d"); // 604800
|
|
99
218
|
|
|
100
|
-
|
|
101
|
-
Useful for token expiry, cache expiry, rate limiting, etc.
|
|
102
|
-
|
|
103
|
-
๐ชช 4. generateTokens()
|
|
104
|
-
|
|
105
|
-
Generates access + refresh token pair.
|
|
106
|
-
|
|
219
|
+
// Generate token pair
|
|
107
220
|
const tokens = generateTokens(
|
|
108
221
|
{ userId: 42 },
|
|
109
222
|
process.env.ACCESS_SECRET!,
|
|
@@ -111,169 +224,130 @@ const tokens = generateTokens(
|
|
|
111
224
|
"15m",
|
|
112
225
|
"7d"
|
|
113
226
|
);
|
|
227
|
+
// Returns: { accessToken: AccessToken, refreshToken: RefreshToken }
|
|
114
228
|
|
|
115
|
-
|
|
116
|
-
console.log(tokens.refreshToken);
|
|
117
|
-
|
|
118
|
-
โป๏ธ 5. rotateRefreshToken()
|
|
119
|
-
|
|
120
|
-
Creates a new refresh token using the old one:
|
|
121
|
-
|
|
122
|
-
import { rotateRefreshToken } from "@naman_deep_singh/security";
|
|
123
|
-
|
|
229
|
+
// Rotate refresh token
|
|
124
230
|
const newRefreshToken = rotateRefreshToken(
|
|
125
231
|
oldRefreshToken,
|
|
126
232
|
process.env.REFRESH_SECRET!
|
|
127
233
|
);
|
|
128
234
|
|
|
235
|
+
// Verify token (throws on error)
|
|
236
|
+
const payload = verifyToken(token, process.env.ACCESS_SECRET!);
|
|
129
237
|
|
|
130
|
-
|
|
131
|
-
โ Generates fresh expiration
|
|
132
|
-
|
|
133
|
-
๐ 6. verifyToken()
|
|
134
|
-
|
|
135
|
-
Throws if token is invalid or expired.
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
const payload = verifyToken(token, process.env.ACCESS_SECRET!);
|
|
139
|
-
console.log("User authenticated:", payload);
|
|
140
|
-
} catch (err) {
|
|
141
|
-
console.error("Invalid or expired token");
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
๐ก 7. safeVerifyToken()
|
|
145
|
-
|
|
146
|
-
Never throws โ returns { valid, payload?, error? }
|
|
147
|
-
|
|
238
|
+
// Safe verify (never throws)
|
|
148
239
|
const result = safeVerifyToken(token, process.env.ACCESS_SECRET!);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
240
|
+
/*
|
|
241
|
+
Returns: {
|
|
242
|
+
valid: true,
|
|
243
|
+
payload: { userId: 1, ... },
|
|
244
|
+
error?: undefined
|
|
154
245
|
}
|
|
246
|
+
*/
|
|
247
|
+
|
|
248
|
+
// Decode without verification
|
|
249
|
+
const decoded = decodeToken(token); // Flexible: null | string | JwtPayload
|
|
250
|
+
const payload = decodeTokenStrict(token); // Throws if not object
|
|
251
|
+
|
|
252
|
+
// Extract token from various sources
|
|
253
|
+
const token = extractToken({
|
|
254
|
+
header: req.headers.authorization, // "Bearer <token>"
|
|
255
|
+
cookies: req.cookies, // { token: "...", accessToken: "..." }
|
|
256
|
+
query: req.query, // { token: "..." }
|
|
257
|
+
body: req.body, // { token: "..." }
|
|
258
|
+
wsMessage: message // string or { token: "..." }
|
|
259
|
+
});
|
|
260
|
+
```
|
|
155
261
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
Throws if payload is not an object.
|
|
167
|
-
|
|
168
|
-
try {
|
|
169
|
-
const payload = decodeTokenStrict(token);
|
|
170
|
-
console.log(payload.userId);
|
|
171
|
-
} catch (e) {
|
|
172
|
-
console.error("Invalid token payload");
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
๐ฐ 9. extractToken()
|
|
176
|
-
|
|
177
|
-
Extracts tokens from:
|
|
178
|
-
|
|
179
|
-
Headers (Authorization: Bearer <token>)
|
|
180
|
-
|
|
181
|
-
Cookies (token, accessToken)
|
|
182
|
-
|
|
183
|
-
Query (?token=...)
|
|
184
|
-
|
|
185
|
-
Body ({ token: "" })
|
|
262
|
+
### JWTManager Class
|
|
263
|
+
```typescript
|
|
264
|
+
const jwtManager = new JWTManager({
|
|
265
|
+
accessSecret: process.env.ACCESS_SECRET!,
|
|
266
|
+
refreshSecret: process.env.REFRESH_SECRET!,
|
|
267
|
+
accessExpiry: "15m",
|
|
268
|
+
refreshExpiry: "7d",
|
|
269
|
+
enableCaching: true, // Optional caching
|
|
270
|
+
maxCacheSize: 100 // Default 100
|
|
271
|
+
});
|
|
186
272
|
|
|
187
|
-
|
|
273
|
+
// Generate tokens
|
|
274
|
+
const tokens = await jwtManager.generateTokens({ userId: 42 });
|
|
188
275
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
header: req.headers.authorization,
|
|
193
|
-
cookies: req.cookies,
|
|
194
|
-
query: req.query,
|
|
195
|
-
body: req.body
|
|
196
|
-
});
|
|
276
|
+
// Verify tokens
|
|
277
|
+
const accessPayload = await jwtManager.verifyAccessToken(tokens.accessToken);
|
|
278
|
+
const refreshPayload = await jwtManager.verifyRefreshToken(tokens.refreshToken);
|
|
197
279
|
|
|
198
|
-
|
|
280
|
+
// Rotate refresh token
|
|
281
|
+
const newRefreshToken = await jwtManager.rotateRefreshToken(oldRefreshToken);
|
|
199
282
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
next();
|
|
203
|
-
} catch (err) {
|
|
204
|
-
return res.status(401).json({ error: "Invalid token" });
|
|
205
|
-
}
|
|
206
|
-
}
|
|
283
|
+
// Decode token
|
|
284
|
+
const decoded = jwtManager.decodeToken(token);
|
|
207
285
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const token = extractToken({ wsMessage: msg });
|
|
286
|
+
// Extract from header
|
|
287
|
+
const token = jwtManager.extractTokenFromHeader("Bearer eyJ...");
|
|
211
288
|
|
|
212
|
-
|
|
289
|
+
// Validate without throwing
|
|
290
|
+
const isValid = jwtManager.validateToken(token, secret);
|
|
213
291
|
|
|
214
|
-
|
|
292
|
+
// Check expiration
|
|
293
|
+
const isExpired = jwtManager.isTokenExpired(token);
|
|
294
|
+
const expiresAt = jwtManager.getTokenExpiration(token);
|
|
215
295
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
296
|
+
// Cache management
|
|
297
|
+
jwtManager.clearCache();
|
|
298
|
+
const stats = jwtManager.getCacheStats(); // { size: 5, maxSize: 100 }
|
|
299
|
+
```
|
|
220
300
|
|
|
221
|
-
|
|
222
|
-
Registration
|
|
223
|
-
async function registerUser(email: string, password: string) {
|
|
224
|
-
const hash = await hashPassword(password);
|
|
301
|
+
## ๐ 3. Crypto Utilities
|
|
225
302
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
303
|
+
### Functional Exports
|
|
304
|
+
```typescript
|
|
305
|
+
// AES-256-GCM Encryption
|
|
306
|
+
const encrypted = encrypt("sensitive data", "your-secret-key");
|
|
307
|
+
// Returns: "iv:encrypted_data"
|
|
231
308
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const valid = await verifyPassword(password, storedHash);
|
|
309
|
+
const decrypted = decrypt(encrypted, "your-secret-key");
|
|
310
|
+
// Returns: "sensitive data"
|
|
235
311
|
|
|
236
|
-
|
|
312
|
+
// HMAC
|
|
313
|
+
const hmac = createHMAC("data", "secret-key");
|
|
314
|
+
const isValidHMAC = verifyHMAC("data", hmac, "secret-key");
|
|
237
315
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
"7d"
|
|
244
|
-
);
|
|
245
|
-
}
|
|
316
|
+
// Random generation
|
|
317
|
+
const randomBytes = generateRandomBytes(32); // Buffer
|
|
318
|
+
const randomString = generateRandomString(16); // Base64 string
|
|
319
|
+
const randomHex = generateRandomHex(32); // Hex string
|
|
320
|
+
```
|
|
246
321
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
oldRefreshToken,
|
|
251
|
-
process.env.REFRESH_SECRET!
|
|
252
|
-
);
|
|
322
|
+
### CryptoManager Class
|
|
323
|
+
```typescript
|
|
324
|
+
const cryptoManager = new CryptoManager("your-secret-key");
|
|
253
325
|
|
|
254
|
-
|
|
326
|
+
// Encryption
|
|
327
|
+
const encrypted = cryptoManager.encrypt("data");
|
|
328
|
+
const decrypted = cryptoManager.decrypt(encrypted);
|
|
255
329
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
"15m"
|
|
260
|
-
);
|
|
330
|
+
// HMAC
|
|
331
|
+
const hmac = cryptoManager.createHMAC("data");
|
|
332
|
+
const isValid = cryptoManager.verifyHMAC("data", hmac);
|
|
261
333
|
|
|
262
|
-
|
|
263
|
-
|
|
334
|
+
// Random
|
|
335
|
+
const randomBytes = cryptoManager.generateRandomBytes(32);
|
|
336
|
+
const randomString = cryptoManager.generateRandomString(16);
|
|
337
|
+
```
|
|
264
338
|
|
|
265
|
-
๐จ Error Handling
|
|
339
|
+
## ๐จ Error Handling
|
|
266
340
|
|
|
267
341
|
This package uses standardized errors from `@naman_deep_singh/errors-utils`:
|
|
268
342
|
|
|
269
343
|
```typescript
|
|
270
|
-
import {
|
|
271
|
-
hashPassword,
|
|
344
|
+
import {
|
|
345
|
+
hashPassword,
|
|
272
346
|
verifyPassword,
|
|
273
|
-
BadRequestError,
|
|
347
|
+
BadRequestError,
|
|
274
348
|
UnauthorizedError,
|
|
275
349
|
ValidationError,
|
|
276
|
-
InternalServerError
|
|
350
|
+
InternalServerError
|
|
277
351
|
} from '@naman_deep_singh/security';
|
|
278
352
|
|
|
279
353
|
try {
|
|
@@ -304,14 +378,118 @@ try {
|
|
|
304
378
|
- `ValidationError` (422) - Password strength validation
|
|
305
379
|
- `InternalServerError` (500) - Server-side processing errors
|
|
306
380
|
|
|
381
|
+
## ๐งฉ Complete Authentication Example
|
|
382
|
+
|
|
383
|
+
### Registration with Validation
|
|
384
|
+
```typescript
|
|
385
|
+
import { PasswordManager, JWTManager } from '@naman_deep_singh/security';
|
|
386
|
+
|
|
387
|
+
const passwordManager = new PasswordManager({
|
|
388
|
+
minLength: 12,
|
|
389
|
+
requireUppercase: true,
|
|
390
|
+
requireNumbers: true,
|
|
391
|
+
requireSpecialChars: true
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const jwtManager = new JWTManager({
|
|
395
|
+
accessSecret: process.env.ACCESS_SECRET!,
|
|
396
|
+
refreshSecret: process.env.REFRESH_SECRET!,
|
|
397
|
+
accessExpiry: "15m",
|
|
398
|
+
refreshExpiry: "7d"
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
async function registerUser(email: string, password: string) {
|
|
402
|
+
// Validate password strength
|
|
403
|
+
const validation = passwordManager.validate(password);
|
|
404
|
+
if (!validation.isValid) {
|
|
405
|
+
throw new ValidationError(`Password validation failed: ${validation.errors.join(', ')}`);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Hash password
|
|
409
|
+
const { hash, salt } = await passwordManager.hash(password);
|
|
410
|
+
|
|
411
|
+
// Store user with hash and salt
|
|
412
|
+
return {
|
|
413
|
+
email,
|
|
414
|
+
passwordHash: hash,
|
|
415
|
+
passwordSalt: salt
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Login with Token Generation
|
|
421
|
+
```typescript
|
|
422
|
+
async function loginUser(email: string, password: string, storedHash: string, storedSalt: string) {
|
|
423
|
+
// Verify password
|
|
424
|
+
const isValid = await passwordManager.verify(password, storedHash, storedSalt);
|
|
425
|
+
if (!isValid) {
|
|
426
|
+
throw new UnauthorizedError("Invalid credentials");
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Generate tokens
|
|
430
|
+
return jwtManager.generateTokens({ email });
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Token Refresh
|
|
435
|
+
```typescript
|
|
436
|
+
async function refreshTokens(oldRefreshToken: string) {
|
|
437
|
+
// Verify old refresh token
|
|
438
|
+
const decoded = await jwtManager.verifyRefreshToken(oldRefreshToken);
|
|
439
|
+
|
|
440
|
+
// Generate new token pair
|
|
441
|
+
const newTokens = await jwtManager.generateTokens(decoded);
|
|
442
|
+
|
|
443
|
+
// Rotate refresh token
|
|
444
|
+
const rotatedRefreshToken = await jwtManager.rotateRefreshToken(oldRefreshToken);
|
|
445
|
+
|
|
446
|
+
return {
|
|
447
|
+
accessToken: newTokens.accessToken,
|
|
448
|
+
refreshToken: rotatedRefreshToken
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Express Middleware
|
|
454
|
+
```typescript
|
|
455
|
+
import { extractToken, safeVerifyToken } from '@naman_deep_singh/security';
|
|
456
|
+
|
|
457
|
+
export function authMiddleware(req, res, next) {
|
|
458
|
+
const token = extractToken({
|
|
459
|
+
header: req.headers.authorization,
|
|
460
|
+
cookies: req.cookies,
|
|
461
|
+
query: req.query,
|
|
462
|
+
body: req.body
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
if (!token) {
|
|
466
|
+
return res.status(401).json({ error: "Token missing" });
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const result = safeVerifyToken(token, process.env.ACCESS_SECRET!);
|
|
470
|
+
|
|
471
|
+
if (!result.valid) {
|
|
472
|
+
return res.status(401).json({ error: "Invalid token" });
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
req.user = result.payload;
|
|
476
|
+
next();
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
307
480
|
๐ Security Best Practices
|
|
308
481
|
|
|
309
|
-
โ Use 32+ character secrets
|
|
482
|
+
โ Use 32+ character secrets for JWT and encryption
|
|
310
483
|
โ Store secrets in environment variables
|
|
311
484
|
โ Always use HTTPS in production
|
|
312
485
|
โ Keep refresh tokens secure (HttpOnly cookie recommended)
|
|
313
486
|
โ Do not store passwords in plain textโever
|
|
487
|
+
โ Use password peppering for additional security
|
|
488
|
+
โ Implement proper password strength requirements
|
|
489
|
+
โ Enable JWT caching for performance (but monitor memory usage)
|
|
314
490
|
โ Handle errors appropriately with proper HTTP status codes
|
|
491
|
+
โ Regularly rotate secrets and tokens
|
|
492
|
+
โ Use secure random generation for all cryptographic operations
|
|
315
493
|
|
|
316
494
|
๐ Integration with Other Packages
|
|
317
495
|
|
|
@@ -319,19 +497,20 @@ try {
|
|
|
319
497
|
|
|
320
498
|
```typescript
|
|
321
499
|
import { createServer } from '@naman_deep_singh/server-utils';
|
|
322
|
-
import {
|
|
500
|
+
import { PasswordManager } from '@naman_deep_singh/security';
|
|
323
501
|
|
|
324
502
|
const server = createServer('Auth API', '1.0.0');
|
|
503
|
+
const passwordManager = new PasswordManager();
|
|
325
504
|
|
|
326
505
|
server.app.post('/register', async (req, res) => {
|
|
327
506
|
try {
|
|
328
507
|
const { password } = req.body;
|
|
329
|
-
const hash = await
|
|
508
|
+
const hash = await passwordManager.hash(password);
|
|
330
509
|
// Save user with hash...
|
|
331
510
|
res.json({ success: true });
|
|
332
511
|
} catch (error) {
|
|
333
512
|
// Errors automatically handled by server-utils middleware
|
|
334
|
-
throw error;
|
|
513
|
+
throw error;
|
|
335
514
|
}
|
|
336
515
|
});
|
|
337
516
|
```
|
|
@@ -348,4 +527,4 @@ server.app.use(expressErrorHandler); // Handles security errors consistently
|
|
|
348
527
|
|
|
349
528
|
๐ License
|
|
350
529
|
|
|
351
|
-
MIT โ free to use & modify.
|
|
530
|
+
MIT โ free to use & modify.
|