@naman_deep_singh/security 1.0.4 → 1.2.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 +315 -80
- package/dist/cjs/core/crypto/decrypt.d.ts +1 -0
- package/dist/cjs/core/crypto/decrypt.js +21 -0
- package/dist/cjs/core/crypto/encrypt.d.ts +1 -0
- package/dist/cjs/core/crypto/encrypt.js +16 -0
- package/dist/cjs/core/crypto/hmac.d.ts +8 -0
- package/dist/cjs/core/crypto/hmac.js +24 -0
- package/dist/cjs/core/crypto/index.d.ts +4 -0
- package/dist/cjs/core/crypto/index.js +20 -0
- package/dist/cjs/core/crypto/random.d.ts +8 -0
- package/dist/cjs/core/crypto/random.js +21 -0
- package/dist/cjs/core/jwt/decode.d.ts +12 -0
- package/dist/cjs/core/jwt/decode.js +25 -0
- package/dist/cjs/core/jwt/extractToken.d.ts +11 -0
- package/dist/cjs/core/jwt/extractToken.js +54 -0
- package/dist/cjs/core/jwt/generateTokens.d.ts +4 -0
- package/dist/cjs/core/jwt/generateTokens.js +30 -0
- package/dist/cjs/core/jwt/index.d.ts +8 -0
- package/dist/cjs/core/jwt/index.js +24 -0
- package/dist/cjs/core/jwt/parseDuration.d.ts +1 -0
- package/dist/cjs/core/jwt/parseDuration.js +29 -0
- package/dist/cjs/core/jwt/signToken.d.ts +2 -0
- package/dist/cjs/core/jwt/signToken.js +26 -0
- package/dist/cjs/core/jwt/types.d.ts +22 -0
- package/dist/cjs/core/jwt/types.js +2 -0
- package/dist/cjs/core/jwt/validateToken.d.ts +13 -0
- package/dist/cjs/core/jwt/validateToken.js +37 -0
- package/dist/cjs/core/jwt/verify.d.ts +18 -0
- package/dist/cjs/core/jwt/verify.js +44 -0
- package/dist/cjs/core/password/hash.d.ts +10 -0
- package/dist/cjs/core/password/hash.js +45 -0
- package/dist/cjs/core/password/index.d.ts +3 -0
- package/dist/cjs/core/password/index.js +19 -0
- package/dist/cjs/core/password/strength.d.ts +2 -0
- package/dist/cjs/core/password/strength.js +21 -0
- package/dist/cjs/core/password/types.d.ts +7 -0
- package/dist/cjs/core/password/types.js +2 -0
- package/dist/cjs/core/password/utils.d.ts +4 -0
- package/dist/cjs/core/password/utils.js +38 -0
- package/dist/cjs/core/password/verify.d.ts +10 -0
- package/dist/cjs/core/password/verify.js +46 -0
- package/dist/cjs/index.d.ts +41 -0
- package/dist/cjs/index.js +56 -0
- package/dist/esm/core/crypto/decrypt.d.ts +1 -0
- package/dist/esm/core/crypto/decrypt.js +14 -0
- package/dist/esm/core/crypto/encrypt.d.ts +1 -0
- package/dist/esm/core/crypto/encrypt.js +9 -0
- package/dist/esm/core/crypto/hmac.d.ts +8 -0
- package/dist/esm/core/crypto/hmac.js +16 -0
- package/dist/esm/core/crypto/index.d.ts +4 -0
- package/dist/esm/core/crypto/index.js +4 -0
- package/dist/esm/core/crypto/random.d.ts +8 -0
- package/dist/esm/core/crypto/random.js +13 -0
- package/dist/esm/core/jwt/decode.d.ts +12 -0
- package/dist/esm/core/jwt/decode.js +21 -0
- package/dist/esm/core/jwt/extractToken.d.ts +11 -0
- package/dist/esm/core/jwt/extractToken.js +51 -0
- package/dist/esm/core/jwt/generateTokens.d.ts +4 -0
- package/dist/esm/core/jwt/generateTokens.js +25 -0
- package/dist/esm/core/jwt/index.d.ts +8 -0
- package/dist/esm/core/jwt/index.js +8 -0
- package/dist/esm/core/jwt/parseDuration.d.ts +1 -0
- package/dist/esm/core/jwt/parseDuration.js +26 -0
- package/dist/esm/core/jwt/signToken.d.ts +2 -0
- package/dist/esm/core/jwt/signToken.js +22 -0
- package/dist/esm/core/jwt/types.d.ts +22 -0
- package/dist/esm/core/jwt/types.js +1 -0
- package/dist/esm/core/jwt/validateToken.d.ts +13 -0
- package/dist/esm/core/jwt/validateToken.js +33 -0
- package/dist/esm/core/jwt/verify.d.ts +18 -0
- package/dist/esm/core/jwt/verify.js +37 -0
- package/dist/esm/core/password/hash.d.ts +10 -0
- package/dist/esm/core/password/hash.js +35 -0
- package/dist/esm/core/password/index.d.ts +3 -0
- package/dist/esm/core/password/index.js +3 -0
- package/dist/esm/core/password/strength.d.ts +2 -0
- package/dist/esm/core/password/strength.js +17 -0
- package/dist/esm/core/password/types.d.ts +7 -0
- package/dist/esm/core/password/types.js +1 -0
- package/dist/esm/core/password/utils.d.ts +4 -0
- package/dist/esm/core/password/utils.js +29 -0
- package/dist/esm/core/password/verify.d.ts +10 -0
- package/dist/esm/core/password/verify.js +36 -0
- package/dist/esm/index.d.ts +41 -0
- package/dist/esm/index.js +13 -0
- package/dist/types/core/crypto/decrypt.d.ts +1 -0
- package/dist/types/core/crypto/encrypt.d.ts +1 -0
- package/dist/types/core/crypto/hmac.d.ts +8 -0
- package/dist/types/core/crypto/index.d.ts +4 -0
- package/dist/types/core/crypto/random.d.ts +8 -0
- package/dist/types/core/jwt/decode.d.ts +12 -0
- package/dist/types/core/jwt/extractToken.d.ts +11 -0
- package/dist/types/core/jwt/generateTokens.d.ts +4 -0
- package/dist/types/core/jwt/index.d.ts +8 -0
- package/dist/types/core/jwt/parseDuration.d.ts +1 -0
- package/dist/types/core/jwt/signToken.d.ts +2 -0
- package/dist/types/core/jwt/types.d.ts +22 -0
- package/dist/types/core/jwt/validateToken.d.ts +13 -0
- package/dist/types/core/jwt/verify.d.ts +18 -0
- package/dist/types/core/password/hash.d.ts +10 -0
- package/dist/types/core/password/index.d.ts +3 -0
- package/dist/types/core/password/strength.d.ts +2 -0
- package/dist/types/core/password/types.d.ts +7 -0
- package/dist/types/core/password/utils.d.ts +4 -0
- package/dist/types/core/password/verify.d.ts +10 -0
- package/dist/types/index.d.ts +41 -0
- package/package.json +23 -7
- package/dist/index.d.ts +0 -16
- package/dist/index.js +0 -41
- package/src/index.ts +0 -39
- package/tsconfig.json +0 -21
package/README.md
CHANGED
|
@@ -1,116 +1,351 @@
|
|
|
1
1
|
# @naman_deep_singh/security
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Version:** 1.2.0
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A complete, lightweight security toolkit for Node.js & TypeScript providing:
|
|
6
|
+
|
|
7
|
+
🔐 **Password hashing & validation** with bcrypt
|
|
8
|
+
🔑 **JWT signing & verification** (no deprecated expiresIn)
|
|
9
|
+
🧮 **Duration parser** ("15m", "7d", etc.)
|
|
10
|
+
🪪 **Token generator** (access + refresh pair)
|
|
11
|
+
♻️ **Refresh token rotation** helper
|
|
12
|
+
🧰 **Robust token extraction** (Headers, Cookies, Query, Body, WebSocket)
|
|
13
|
+
🧩 **Safe & strict JWT decode** utilities
|
|
14
|
+
🚨 **Standardized error handling** with @naman_deep_singh/errors-utils
|
|
15
|
+
|
|
16
|
+
✔ **Fully typed** with TypeScript
|
|
17
|
+
✔ **Branded token types** for compile-time safety (AccessToken/RefreshToken)
|
|
18
|
+
✔ **Structured verification results** for better error handling
|
|
19
|
+
✔ **Enhanced verification options** with flexible configuration
|
|
20
|
+
✔ **Consistent errors** across your application ecosystem
|
|
21
|
+
✔ **Works in both ESM and CommonJS**
|
|
6
22
|
|
|
7
23
|
```bash
|
|
24
|
+
|
|
25
|
+
📦 Installation
|
|
8
26
|
npm install @naman_deep_singh/security
|
|
9
|
-
```
|
|
10
27
|
|
|
11
|
-
|
|
28
|
+
🔧 Features
|
|
12
29
|
|
|
13
|
-
|
|
14
|
-
- ✅ **JWT token management** with configurable expiration
|
|
15
|
-
- ✅ **TypeScript support** with full type safety
|
|
16
|
-
- ✅ **Hybrid exports** - use named imports or namespace imports
|
|
17
|
-
- ✅ **Backward compatibility** with legacy function names
|
|
18
|
-
- ✅ **Async/await support** for all operations
|
|
30
|
+
🔥 Password Hashing — secure & async (bcrypt with 10 salt rounds)
|
|
19
31
|
|
|
20
|
-
|
|
32
|
+
🔥 Custom Expiry JWT — manual exp support using duration strings
|
|
21
33
|
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
import { hashPassword, verifyPassword, generateToken, verifyToken } from '@naman_deep_singh/security';
|
|
34
|
+
🔥 Token Pair Generation (accessToken + refreshToken)
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
const hashedPassword = await hashPassword('mypassword');
|
|
28
|
-
const isValid = await verifyPassword('mypassword', hashedPassword);
|
|
36
|
+
🔥 Refresh Token Rotation
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
const token = generateToken({ userId: 1, role: 'admin' }, 'your-secret-key', '24h');
|
|
32
|
-
const decoded = verifyToken(token, 'your-secret-key');
|
|
33
|
-
```
|
|
38
|
+
🔥 Safe & Unsafe JWT Verification
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
import SecurityUtils from '@naman_deep_singh/security';
|
|
40
|
+
🔥 Strict vs Flexible Decoding
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
const token = SecurityUtils.generateToken({ userId: 1 }, 'secret');
|
|
41
|
-
```
|
|
42
|
+
🔥 Universal Token Extraction (Headers, Cookies, Query, Body, WebSocket)
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
import { comparePassword, signToken } from '@naman_deep_singh/security';
|
|
44
|
+
🔥 Duration Parser ("15m", "1h", "7d")
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
const isValid = await comparePassword('password', 'hash');
|
|
49
|
-
const token = signToken({ userId: 1 }, 'secret');
|
|
50
|
-
```
|
|
46
|
+
🔥 Production-grade types
|
|
51
47
|
|
|
52
|
-
|
|
48
|
+
📘 Quick Start
|
|
49
|
+
import {
|
|
50
|
+
hashPassword,
|
|
51
|
+
verifyPassword,
|
|
52
|
+
generateTokens,
|
|
53
|
+
verifyToken,
|
|
54
|
+
safeVerifyToken,
|
|
55
|
+
extractToken
|
|
56
|
+
} from "@naman_deep_singh/security";
|
|
53
57
|
|
|
54
|
-
|
|
55
|
-
- `hashPassword(password: string): Promise<string>` - Hash a password using bcrypt with salt rounds 10
|
|
56
|
-
- `verifyPassword(password: string, hash: string): Promise<boolean>` - Verify password against hash
|
|
57
|
-
- `comparePassword(password: string, hash: string): Promise<boolean>` - Alias for verifyPassword (backward compatibility)
|
|
58
|
+
📚 API Documentation
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
- `generateToken(payload: Record<string, unknown>, secret: Secret, expiresIn?: string): string` - Generate JWT token
|
|
61
|
-
- `verifyToken(token: string, secret: Secret): string | JwtPayload` - Verify and decode JWT token
|
|
62
|
-
- `signToken(payload: Record<string, unknown>, secret: Secret, expiresIn?: string): string` - Alias for generateToken (backward compatibility)
|
|
60
|
+
Below is a complete reference with full usage examples.
|
|
63
61
|
|
|
64
|
-
|
|
62
|
+
🧂 1. Password Utilities
|
|
63
|
+
hashPassword(password: string): Promise<string>
|
|
64
|
+
const hashed = await hashPassword("mypassword");
|
|
65
|
+
console.log(hashed); // $2a$10$...
|
|
65
66
|
|
|
66
|
-
### Complete Authentication Flow
|
|
67
|
-
```typescript
|
|
68
|
-
import { hashPassword, verifyPassword, generateToken, verifyToken } from '@naman_deep_singh/security';
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
verifyPassword(password: string, hash: string): Promise<boolean>
|
|
69
|
+
const isValid = await verifyPassword("mypassword", hashed);
|
|
70
|
+
if (isValid) console.log("Correct password");
|
|
71
|
+
|
|
72
|
+
// Synchronous version also available
|
|
73
|
+
const isValidSync = verifyPasswordSync("mypassword", hashed);
|
|
74
|
+
|
|
75
|
+
🔑 2. JWT Signing
|
|
76
|
+
signToken(payload, secret, expiresIn, options)
|
|
77
|
+
|
|
78
|
+
Creates a JWT with custom exp logic ("15m", "1h", "7d")
|
|
79
|
+
|
|
80
|
+
const token = signToken(
|
|
81
|
+
{ userId: 1 },
|
|
82
|
+
process.env.JWT_SECRET!,
|
|
83
|
+
"1h"
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
console.log(token);
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
✔ No deprecated expiresIn from jsonwebtoken
|
|
90
|
+
✔ Expiration is injected manually via exp
|
|
91
|
+
|
|
92
|
+
🧮 3. parseDuration()
|
|
93
|
+
|
|
94
|
+
Parses duration strings into seconds.
|
|
95
|
+
|
|
96
|
+
parseDuration("15m"); // 900
|
|
97
|
+
parseDuration("2h"); // 7200
|
|
98
|
+
parseDuration("7d"); // 604800
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
Useful for token expiry, cache expiry, rate limiting, etc.
|
|
102
|
+
|
|
103
|
+
🪪 4. generateTokens()
|
|
104
|
+
|
|
105
|
+
Generates access + refresh token pair.
|
|
106
|
+
|
|
107
|
+
const tokens = generateTokens(
|
|
108
|
+
{ userId: 42 },
|
|
109
|
+
process.env.ACCESS_SECRET!,
|
|
110
|
+
process.env.REFRESH_SECRET!,
|
|
111
|
+
"15m",
|
|
112
|
+
"7d"
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
console.log(tokens.accessToken);
|
|
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
|
+
|
|
124
|
+
const newRefreshToken = rotateRefreshToken(
|
|
125
|
+
oldRefreshToken,
|
|
126
|
+
process.env.REFRESH_SECRET!
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
✔ Automatically removes old exp and iat
|
|
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
|
+
|
|
148
|
+
const result = safeVerifyToken(token, process.env.ACCESS_SECRET!);
|
|
149
|
+
|
|
150
|
+
if (!result.valid) {
|
|
151
|
+
console.log("Token invalid:", result.error);
|
|
152
|
+
} else {
|
|
153
|
+
console.log("Token OK:", result.payload);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
🧬 8. Decoding Helpers
|
|
157
|
+
decodeToken(token)
|
|
158
|
+
|
|
159
|
+
Flexible — returns null | string | JwtPayload
|
|
160
|
+
|
|
161
|
+
const decoded = decodeToken(token);
|
|
162
|
+
console.log(decoded);
|
|
163
|
+
|
|
164
|
+
decodeTokenStrict(token)
|
|
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: "" })
|
|
186
|
+
|
|
187
|
+
WebSocket messages (string or object)
|
|
188
|
+
|
|
189
|
+
Example: Express middleware
|
|
190
|
+
export function authMiddleware(req, res, next) {
|
|
191
|
+
const token = extractToken({
|
|
192
|
+
header: req.headers.authorization,
|
|
193
|
+
cookies: req.cookies,
|
|
194
|
+
query: req.query,
|
|
195
|
+
body: req.body
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (!token) return res.status(401).json({ error: "Token missing" });
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
req.user = verifyToken(token, process.env.ACCESS_SECRET!);
|
|
202
|
+
next();
|
|
203
|
+
} catch (err) {
|
|
204
|
+
return res.status(401).json({ error: "Invalid token" });
|
|
205
|
+
}
|
|
75
206
|
}
|
|
76
207
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
if (!
|
|
82
|
-
|
|
208
|
+
Example: WebSocket (ws library)
|
|
209
|
+
ws.on("message", (msg) => {
|
|
210
|
+
const token = extractToken({ wsMessage: msg });
|
|
211
|
+
|
|
212
|
+
if (!token) return;
|
|
213
|
+
|
|
214
|
+
const result = safeVerifyToken(token, process.env.ACCESS_SECRET!);
|
|
215
|
+
|
|
216
|
+
if (result.valid) {
|
|
217
|
+
console.log("WS authenticated user:", result.payload);
|
|
83
218
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
🧩 10. Full Authentication Example
|
|
222
|
+
Registration
|
|
223
|
+
async function registerUser(email: string, password: string) {
|
|
224
|
+
const hash = await hashPassword(password);
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
email,
|
|
228
|
+
passwordHash: hash
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
Login
|
|
233
|
+
async function loginUser(email, password, storedHash) {
|
|
234
|
+
const valid = await verifyPassword(password, storedHash);
|
|
235
|
+
|
|
236
|
+
if (!valid) throw new Error("Invalid credentials");
|
|
237
|
+
|
|
238
|
+
return generateTokens(
|
|
239
|
+
{ email },
|
|
240
|
+
process.env.ACCESS_SECRET!,
|
|
241
|
+
process.env.REFRESH_SECRET!,
|
|
242
|
+
"15m",
|
|
243
|
+
"7d"
|
|
89
244
|
);
|
|
90
|
-
|
|
91
|
-
return { token };
|
|
92
245
|
}
|
|
93
246
|
|
|
94
|
-
|
|
95
|
-
function
|
|
247
|
+
Token Refresh
|
|
248
|
+
function refresh(oldRefreshToken) {
|
|
249
|
+
const newRefreshToken = rotateRefreshToken(
|
|
250
|
+
oldRefreshToken,
|
|
251
|
+
process.env.REFRESH_SECRET!
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const decoded = decodeTokenStrict(oldRefreshToken);
|
|
255
|
+
|
|
256
|
+
const newAccessToken = signToken(
|
|
257
|
+
{ userId: decoded.userId },
|
|
258
|
+
process.env.ACCESS_SECRET!,
|
|
259
|
+
"15m"
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
return { accessToken: newAccessToken, refreshToken: newRefreshToken };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
🚨 Error Handling
|
|
266
|
+
|
|
267
|
+
This package uses standardized errors from `@naman_deep_singh/errors-utils`:
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
import {
|
|
271
|
+
hashPassword,
|
|
272
|
+
verifyPassword,
|
|
273
|
+
BadRequestError,
|
|
274
|
+
UnauthorizedError,
|
|
275
|
+
ValidationError,
|
|
276
|
+
InternalServerError
|
|
277
|
+
} from '@naman_deep_singh/security';
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
const hash = await hashPassword('mypassword');
|
|
281
|
+
} catch (error) {
|
|
282
|
+
if (error instanceof BadRequestError) {
|
|
283
|
+
// Invalid password input (400)
|
|
284
|
+
console.log('Invalid password provided');
|
|
285
|
+
} else if (error instanceof InternalServerError) {
|
|
286
|
+
// Hashing failed (500)
|
|
287
|
+
console.log('Server error during hashing');
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
const isValid = await verifyPassword('password', hash);
|
|
293
|
+
} catch (error) {
|
|
294
|
+
if (error instanceof UnauthorizedError) {
|
|
295
|
+
// Password verification failed (401)
|
|
296
|
+
console.log('Invalid credentials');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Error Types:**
|
|
302
|
+
- `BadRequestError` (400) - Invalid input data
|
|
303
|
+
- `UnauthorizedError` (401) - Authentication failures
|
|
304
|
+
- `ValidationError` (422) - Password strength validation
|
|
305
|
+
- `InternalServerError` (500) - Server-side processing errors
|
|
306
|
+
|
|
307
|
+
🔐 Security Best Practices
|
|
308
|
+
|
|
309
|
+
✔ Use 32+ character secrets
|
|
310
|
+
✔ Store secrets in environment variables
|
|
311
|
+
✔ Always use HTTPS in production
|
|
312
|
+
✔ Keep refresh tokens secure (HttpOnly cookie recommended)
|
|
313
|
+
✔ Do not store passwords in plain text—ever
|
|
314
|
+
✔ Handle errors appropriately with proper HTTP status codes
|
|
315
|
+
|
|
316
|
+
🔗 Integration with Other Packages
|
|
317
|
+
|
|
318
|
+
### With @naman_deep_singh/server-utils
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
import { createServer } from '@naman_deep_singh/server-utils';
|
|
322
|
+
import { hashPassword, verifyPassword } from '@naman_deep_singh/security';
|
|
323
|
+
|
|
324
|
+
const server = createServer('Auth API', '1.0.0');
|
|
325
|
+
|
|
326
|
+
server.app.post('/register', async (req, res) => {
|
|
96
327
|
try {
|
|
97
|
-
const
|
|
98
|
-
|
|
328
|
+
const { password } = req.body;
|
|
329
|
+
const hash = await hashPassword(password);
|
|
330
|
+
// Save user with hash...
|
|
331
|
+
res.json({ success: true });
|
|
99
332
|
} catch (error) {
|
|
100
|
-
|
|
333
|
+
// Errors automatically handled by server-utils middleware
|
|
334
|
+
throw error; // Will be caught and formatted consistently
|
|
101
335
|
}
|
|
102
|
-
}
|
|
336
|
+
});
|
|
103
337
|
```
|
|
104
338
|
|
|
105
|
-
|
|
339
|
+
### With @naman_deep_singh/errors-utils + @naman_deep_singh/response-utils
|
|
106
340
|
|
|
107
|
-
|
|
108
|
-
|
|
341
|
+
```typescript
|
|
342
|
+
import { expressErrorHandler } from '@naman_deep_singh/errors-utils';
|
|
343
|
+
import { responderMiddleware } from '@naman_deep_singh/response-utils';
|
|
344
|
+
|
|
345
|
+
server.app.use(responderMiddleware());
|
|
346
|
+
server.app.use(expressErrorHandler); // Handles security errors consistently
|
|
347
|
+
```
|
|
109
348
|
|
|
110
|
-
|
|
349
|
+
📜 License
|
|
111
350
|
|
|
112
|
-
|
|
113
|
-
2. **Set appropriate expiration times** for tokens
|
|
114
|
-
3. **Store JWT secrets in environment variables**
|
|
115
|
-
4. **Never log or expose hashed passwords**
|
|
116
|
-
5. **Use HTTPS** in production for token transmission
|
|
351
|
+
MIT — free to use & modify.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const decrypt: (data: string, secret: string) => string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.decrypt = void 0;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
const ALGO = "AES-256-GCM";
|
|
9
|
+
const decrypt = (data, secret) => {
|
|
10
|
+
const [ivHex, encryptedHex] = data.split(":");
|
|
11
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
12
|
+
const encrypted = Buffer.from(encryptedHex, "hex");
|
|
13
|
+
const key = crypto_1.default.createHash("sha256").update(secret).digest();
|
|
14
|
+
const decipher = crypto_1.default.createDecipheriv(ALGO, key, iv);
|
|
15
|
+
const decrypted = Buffer.concat([
|
|
16
|
+
decipher.update(encrypted),
|
|
17
|
+
decipher.final(),
|
|
18
|
+
]);
|
|
19
|
+
return decrypted.toString("utf8");
|
|
20
|
+
};
|
|
21
|
+
exports.decrypt = decrypt;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const encrypt: (text: string, secret: string) => string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.encrypt = void 0;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
const ALGO = "AES-256-GCM";
|
|
9
|
+
const encrypt = (text, secret) => {
|
|
10
|
+
const key = crypto_1.default.createHash("sha256").update(secret).digest();
|
|
11
|
+
const iv = crypto_1.default.randomBytes(16);
|
|
12
|
+
const cipher = crypto_1.default.createCipheriv(ALGO, key, iv);
|
|
13
|
+
const encrypted = Buffer.concat([cipher.update(text, "utf8"), cipher.final()]);
|
|
14
|
+
return `${iv.toString("hex")}:${encrypted.toString("hex")}`;
|
|
15
|
+
};
|
|
16
|
+
exports.encrypt = encrypt;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.hmacVerify = exports.hmacSign = void 0;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
/**
|
|
9
|
+
* Sign message using HMAC SHA-256
|
|
10
|
+
*/
|
|
11
|
+
const hmacSign = (message, secret) => {
|
|
12
|
+
return crypto_1.default.createHmac("sha256", secret).update(message).digest("hex");
|
|
13
|
+
};
|
|
14
|
+
exports.hmacSign = hmacSign;
|
|
15
|
+
/**
|
|
16
|
+
* Verify HMAC signature
|
|
17
|
+
*/
|
|
18
|
+
const hmacVerify = (message, secret, signature) => {
|
|
19
|
+
const expected = (0, exports.hmacSign)(message, secret);
|
|
20
|
+
if (signature.length !== expected.length)
|
|
21
|
+
return false;
|
|
22
|
+
return crypto_1.default.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
|
|
23
|
+
};
|
|
24
|
+
exports.hmacVerify = hmacVerify;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./decrypt"), exports);
|
|
18
|
+
__exportStar(require("./encrypt"), exports);
|
|
19
|
+
__exportStar(require("./hmac"), exports);
|
|
20
|
+
__exportStar(require("./random"), exports);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateStrongPassword = exports.randomToken = void 0;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
/**
|
|
9
|
+
* Generate cryptographically secure random string
|
|
10
|
+
*/
|
|
11
|
+
const randomToken = (length = 32) => {
|
|
12
|
+
return crypto_1.default.randomBytes(length).toString("hex");
|
|
13
|
+
};
|
|
14
|
+
exports.randomToken = randomToken;
|
|
15
|
+
/**
|
|
16
|
+
* Generate a strong random password
|
|
17
|
+
*/
|
|
18
|
+
const generateStrongPassword = (length = 16) => {
|
|
19
|
+
return crypto_1.default.randomBytes(length).toString("hex").slice(0, length);
|
|
20
|
+
};
|
|
21
|
+
exports.generateStrongPassword = generateStrongPassword;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { JwtPayload } from "jsonwebtoken";
|
|
2
|
+
/**
|
|
3
|
+
* Flexible decode
|
|
4
|
+
* Returns: null | string | JwtPayload
|
|
5
|
+
* Mirrors jsonwebtoken.decode()
|
|
6
|
+
*/
|
|
7
|
+
export declare function decodeToken(token: string): null | string | JwtPayload;
|
|
8
|
+
/**
|
|
9
|
+
* Strict decode
|
|
10
|
+
* Always returns JwtPayload or throws error
|
|
11
|
+
*/
|
|
12
|
+
export declare function decodeTokenStrict(token: string): JwtPayload;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeToken = decodeToken;
|
|
4
|
+
exports.decodeTokenStrict = decodeTokenStrict;
|
|
5
|
+
// src/jwt/decodeToken.ts
|
|
6
|
+
const jsonwebtoken_1 = require("jsonwebtoken");
|
|
7
|
+
/**
|
|
8
|
+
* Flexible decode
|
|
9
|
+
* Returns: null | string | JwtPayload
|
|
10
|
+
* Mirrors jsonwebtoken.decode()
|
|
11
|
+
*/
|
|
12
|
+
function decodeToken(token) {
|
|
13
|
+
return (0, jsonwebtoken_1.decode)(token);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Strict decode
|
|
17
|
+
* Always returns JwtPayload or throws error
|
|
18
|
+
*/
|
|
19
|
+
function decodeTokenStrict(token) {
|
|
20
|
+
const decoded = (0, jsonwebtoken_1.decode)(token);
|
|
21
|
+
if (!decoded || typeof decoded === "string") {
|
|
22
|
+
throw new Error("Invalid JWT payload structure");
|
|
23
|
+
}
|
|
24
|
+
return decoded;
|
|
25
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface TokenSources {
|
|
2
|
+
header?: string | undefined | null;
|
|
3
|
+
cookies?: Record<string, string> | undefined;
|
|
4
|
+
query?: Record<string, string | undefined> | undefined;
|
|
5
|
+
body?: Record<string, unknown> | undefined;
|
|
6
|
+
wsMessage?: string | Record<string, unknown> | undefined;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Universal token extractor
|
|
10
|
+
*/
|
|
11
|
+
export declare function extractToken(sources: TokenSources): string | null;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractToken = extractToken;
|
|
4
|
+
/**
|
|
5
|
+
* Universal token extractor
|
|
6
|
+
*/
|
|
7
|
+
function extractToken(sources) {
|
|
8
|
+
const { header, cookies, query, body, wsMessage } = sources;
|
|
9
|
+
// 1. Authorization: Bearer <token>
|
|
10
|
+
if (header) {
|
|
11
|
+
const parts = header.split(" ");
|
|
12
|
+
if (parts.length === 2 && parts[0] === "Bearer")
|
|
13
|
+
return parts[1];
|
|
14
|
+
}
|
|
15
|
+
// 2. Cookies: token / accessToken
|
|
16
|
+
if (cookies) {
|
|
17
|
+
if (cookies["token"])
|
|
18
|
+
return cookies["token"];
|
|
19
|
+
if (cookies["accessToken"])
|
|
20
|
+
return cookies["accessToken"];
|
|
21
|
+
}
|
|
22
|
+
// 3. Query params: ?token=xxx
|
|
23
|
+
if (query?.token)
|
|
24
|
+
return query.token;
|
|
25
|
+
// 4. Body: { token: "" }
|
|
26
|
+
if (body?.token && typeof body.token === 'string')
|
|
27
|
+
return body.token;
|
|
28
|
+
// 5. WebSocket message extraction (NEW)
|
|
29
|
+
if (wsMessage) {
|
|
30
|
+
try {
|
|
31
|
+
let msg = wsMessage;
|
|
32
|
+
// If it's a JSON string → parse safely
|
|
33
|
+
if (typeof wsMessage === "string") {
|
|
34
|
+
msg = JSON.parse(wsMessage);
|
|
35
|
+
}
|
|
36
|
+
// Ensure msg is an object before property access
|
|
37
|
+
if (typeof msg === 'object' && msg !== null) {
|
|
38
|
+
const m = msg;
|
|
39
|
+
if (typeof m['token'] === 'string')
|
|
40
|
+
return m['token'];
|
|
41
|
+
const auth = m['auth'];
|
|
42
|
+
if (typeof auth === 'object' && auth !== null) {
|
|
43
|
+
const a = auth;
|
|
44
|
+
if (typeof a['token'] === 'string')
|
|
45
|
+
return a['token'];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Ignore parse errors gracefully
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Secret } from "jsonwebtoken";
|
|
2
|
+
import { RefreshToken, TokenPair } from "./types";
|
|
3
|
+
export declare const generateTokens: (payload: Record<string, unknown>, accessSecret: Secret, refreshSecret: Secret, accessExpiry?: string | number, refreshExpiry?: string | number) => TokenPair;
|
|
4
|
+
export declare function rotateRefreshToken(oldToken: string, secret: Secret): RefreshToken;
|