@igxjs/node-components 1.0.6 → 1.0.8
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 +75 -303
- package/components/http-handlers.js +3 -1
- package/components/jwt.js +104 -0
- package/docs/README.md +54 -0
- package/docs/flex-router.md +167 -0
- package/docs/http-handlers.md +302 -0
- package/docs/jwt-manager.md +124 -0
- package/docs/redis-manager.md +210 -0
- package/docs/session-manager.md +160 -0
- package/index.d.ts +105 -0
- package/index.js +2 -1
- package/package.json +1 -1
- package/tests/jwt.test.js +345 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Node Components
|
|
2
2
|
|
|
3
|
-
Shared components for Express.js applications providing session management, routing utilities, error handling, and Redis integration.
|
|
3
|
+
Shared components for Express.js applications providing session management, routing utilities, error handling, JWT authentication, and Redis integration.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,349 +8,112 @@ Shared components for Express.js applications providing session management, rout
|
|
|
8
8
|
npm install @igxjs/node-components
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Components
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
| Component | Description | Documentation |
|
|
14
|
+
|-----------|-------------|---------------|
|
|
15
|
+
| **SessionManager** | SSO session management with Redis/memory storage | [View docs](./docs/session-manager.md) |
|
|
16
|
+
| **FlexRouter** | Flexible routing with context paths and middleware | [View docs](./docs/flex-router.md) |
|
|
17
|
+
| **RedisManager** | Redis connection management with TLS support | [View docs](./docs/redis-manager.md) |
|
|
18
|
+
| **JWT Manager** | Secure JWT encryption/decryption with JWE | [View docs](./docs/jwt-manager.md) |
|
|
19
|
+
| **HTTP Handlers** | Standardized error handling and status codes | [View docs](./docs/http-handlers.md) |
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
## Quick Start Examples
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
### SessionManager
|
|
18
24
|
|
|
19
25
|
```javascript
|
|
20
|
-
// Example configuration object
|
|
21
|
-
// All fields are strings except SESSION_AGE which is a number
|
|
22
|
-
|
|
23
|
-
const config = {
|
|
24
|
-
// SSO Configuration
|
|
25
|
-
SSO_ENDPOINT_URL: 'https://sso.example.com',
|
|
26
|
-
SSO_CLIENT_ID: 'your-client-id',
|
|
27
|
-
SSO_CLIENT_SECRET: 'your-client-secret',
|
|
28
|
-
SSO_SUCCESS_URL: '/dashboard',
|
|
29
|
-
SSO_FAILURE_URL: '/login',
|
|
30
|
-
|
|
31
|
-
// Session Configuration
|
|
32
|
-
SESSION_AGE: 64800000, // 18 hours in milliseconds
|
|
33
|
-
SESSION_COOKIE_PATH: '/',
|
|
34
|
-
SESSION_SECRET: 'your-session-secret',
|
|
35
|
-
SESSION_PREFIX: 'ibmid:', // Default value when not provided
|
|
36
|
-
|
|
37
|
-
// Redis Configuration (optional - uses memory store if not provided)
|
|
38
|
-
REDIS_URL: 'redis://localhost:6379',
|
|
39
|
-
REDIS_CERT_PATH: '/path/to/cert.pem' // For TLS connections
|
|
40
|
-
};
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
#### Usage Example
|
|
44
|
-
|
|
45
|
-
```javascript
|
|
46
|
-
import express from 'express';
|
|
47
26
|
import { SessionManager } from '@igxjs/node-components';
|
|
48
27
|
|
|
49
|
-
|
|
50
|
-
const session = new SessionManager(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
...user,
|
|
57
|
-
displayName: user.email?.split('@')[0],
|
|
58
|
-
hasAdminAccess: user.authorized && user.email?.endsWith('@admin.com')
|
|
59
|
-
};
|
|
28
|
+
// Create singleton instance
|
|
29
|
+
export const session = new SessionManager({
|
|
30
|
+
SSO_ENDPOINT_URL: process.env.SSO_ENDPOINT_URL,
|
|
31
|
+
SSO_CLIENT_ID: process.env.SSO_CLIENT_ID,
|
|
32
|
+
SSO_CLIENT_SECRET: process.env.SSO_CLIENT_SECRET,
|
|
33
|
+
SESSION_SECRET: process.env.SESSION_SECRET,
|
|
34
|
+
REDIS_URL: process.env.REDIS_URL
|
|
60
35
|
});
|
|
61
36
|
|
|
62
|
-
//
|
|
37
|
+
// Setup in your app
|
|
38
|
+
await session.setup(app, (user) => ({ ...user, displayName: user.email }));
|
|
39
|
+
|
|
40
|
+
// Protect routes
|
|
63
41
|
app.get('/protected', session.authenticate(), (req, res) => {
|
|
64
42
|
res.json({ user: req.user });
|
|
65
43
|
});
|
|
66
|
-
|
|
67
|
-
// SSO callback endpoint
|
|
68
|
-
app.get('/auth/callback', session.callback((user) => {
|
|
69
|
-
// Initialize user object after successful login
|
|
70
|
-
return {
|
|
71
|
-
...user,
|
|
72
|
-
loginTime: new Date()
|
|
73
|
-
};
|
|
74
|
-
}));
|
|
75
|
-
|
|
76
|
-
// Get available identity providers
|
|
77
|
-
app.get('/auth/providers', session.identityProviders());
|
|
78
|
-
|
|
79
|
-
// Refresh user session
|
|
80
|
-
app.post('/auth/refresh', session.refresh((user) => {
|
|
81
|
-
return { ...user, refreshedAt: new Date() };
|
|
82
|
-
}));
|
|
83
|
-
|
|
84
|
-
// Logout endpoint
|
|
85
|
-
app.get('/auth/logout', session.logout());
|
|
86
44
|
```
|
|
87
45
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
- **`setup(app, updateUser)`** - Initialize session configurations
|
|
91
|
-
- **`authenticate(isDebugging?, redirectUrl?)`** - Resource protection middleware
|
|
92
|
-
- **`callback(initUser)`** - SSO callback handler for successful login
|
|
93
|
-
- **`identityProviders()`** - Get available identity providers
|
|
94
|
-
- **`logout()`** - Application logout handler (not SSO logout)
|
|
95
|
-
- **`refresh(initUser)`** - Refresh user session with new token
|
|
96
|
-
- **`redisManager()`** - Get the RedisManager instance (returns RedisManager or null)
|
|
97
|
-
- **`hasLock(email)`** - Check if email has a session refresh lock
|
|
98
|
-
- **`lock(email)`** - Lock email for session refresh (prevents concurrent refreshes)
|
|
99
|
-
- **`clearLocks()`** - Clear expired session refresh locks
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
### 2. FlexRouter
|
|
46
|
+
[📖 Full SessionManager Documentation](./docs/session-manager.md)
|
|
104
47
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
#### Usage Example
|
|
48
|
+
### FlexRouter
|
|
108
49
|
|
|
109
50
|
```javascript
|
|
110
51
|
import { Router } from 'express';
|
|
111
52
|
import { FlexRouter } from '@igxjs/node-components';
|
|
112
|
-
// Assuming you have an authenticate middleware
|
|
113
|
-
// import { authenticate } from './middlewares/auth.js';
|
|
114
|
-
|
|
115
|
-
// Create routers
|
|
116
|
-
const publicRouter = Router();
|
|
117
|
-
const privateRouter = Router();
|
|
118
|
-
|
|
119
|
-
publicRouter.get('/health', (req, res) => {
|
|
120
|
-
res.send('OK');
|
|
121
|
-
});
|
|
122
53
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// Define flex routers with context paths and optional middleware
|
|
128
|
-
export const routers = [
|
|
129
|
-
new FlexRouter('/api/v1/public', publicRouter),
|
|
130
|
-
new FlexRouter('/api/v1/protected', privateRouter, [authenticate]), // with middleware
|
|
131
|
-
];
|
|
54
|
+
const apiRouter = Router();
|
|
55
|
+
apiRouter.get('/users', (req, res) => res.json({ users: [] }));
|
|
132
56
|
|
|
133
|
-
// Mount
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
routers.forEach(router => {
|
|
138
|
-
router.mount(app, basePath);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
// Routes will be available at:
|
|
142
|
-
// - /api/v1/public/health
|
|
143
|
-
// - /api/v1/protected/users (with authenticate middleware)
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
#### API
|
|
147
|
-
|
|
148
|
-
- **`constructor(context, router, handlers?)`** - Create a new FlexRouter
|
|
149
|
-
- `context` (string) - The context path for the router
|
|
150
|
-
- `router` - Express Router instance
|
|
151
|
-
- `handlers` - Optional array of middleware handlers
|
|
152
|
-
|
|
153
|
-
- **`mount(app, basePath)`** - Mount the router to an Express application
|
|
154
|
-
- `app` - Express application instance
|
|
155
|
-
- `basePath` (string) - Base path to prepend to the context
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
### 3. RedisManager
|
|
160
|
-
|
|
161
|
-
Redis connection management with TLS support and automatic reconnection handling.
|
|
162
|
-
|
|
163
|
-
* Note: the RedisManager is used internally by the `SessionManager`, so you don't need to use it directly.
|
|
164
|
-
|
|
165
|
-
#### Usage Example
|
|
166
|
-
|
|
167
|
-
```javascript
|
|
168
|
-
import { RedisManager } from '@igxjs/node-components';
|
|
169
|
-
|
|
170
|
-
const redisManager = new RedisManager();
|
|
171
|
-
|
|
172
|
-
// Connect to Redis (with optional TLS certificate)
|
|
173
|
-
const connected = await redisManager.connect(
|
|
174
|
-
'rediss://localhost:6379',
|
|
175
|
-
'/path/to/cert.pem'
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
if (connected) {
|
|
179
|
-
// Get Redis client for direct operations
|
|
180
|
-
const client = redisManager.getClient();
|
|
181
|
-
await client.set('key', 'value');
|
|
182
|
-
|
|
183
|
-
// Check connection status
|
|
184
|
-
const isConnected = await redisManager.isConnected();
|
|
185
|
-
|
|
186
|
-
// Disconnect when done
|
|
187
|
-
await redisManager.disConnect();
|
|
188
|
-
}
|
|
57
|
+
// Mount with context path and middleware
|
|
58
|
+
const flexRouter = new FlexRouter('/api/v1', apiRouter, [authenticate]);
|
|
59
|
+
flexRouter.mount(app, '');
|
|
189
60
|
```
|
|
190
61
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
- **`connect(redisUrl, certPath)`** - Connect to Redis server
|
|
194
|
-
- Returns: `Promise<boolean>` - Returns true if connected successfully
|
|
195
|
-
- Supports both `redis://` and `rediss://` (TLS) URLs
|
|
196
|
-
- Automatically handles TLS certificate loading when using `rediss://`
|
|
197
|
-
|
|
198
|
-
- **`getClient()`** - Get the Redis client instance
|
|
199
|
-
- Returns: Redis client for direct operations
|
|
200
|
-
|
|
201
|
-
- **`isConnected()`** - Check if Redis connection is active
|
|
202
|
-
- Returns: `Promise<boolean>` - Returns true if connected and responsive
|
|
203
|
-
|
|
204
|
-
- **`disConnect()`** - Disconnect from Redis server
|
|
205
|
-
- Returns: `Promise<void>`
|
|
206
|
-
|
|
207
|
-
---
|
|
62
|
+
[📖 Full FlexRouter Documentation](./docs/flex-router.md)
|
|
208
63
|
|
|
209
|
-
###
|
|
210
|
-
|
|
211
|
-
Custom error handling utilities with standardized HTTP status codes and error responses.
|
|
212
|
-
|
|
213
|
-
#### Available Exports
|
|
64
|
+
### JWT Manager
|
|
214
65
|
|
|
215
66
|
```javascript
|
|
216
|
-
import {
|
|
217
|
-
CustomError,
|
|
218
|
-
httpErrorHandler,
|
|
219
|
-
httpCodes,
|
|
220
|
-
httpMessages,
|
|
221
|
-
httpHelper
|
|
222
|
-
} from '@igxjs/node-components';
|
|
223
|
-
```
|
|
67
|
+
import { JwtManager } from '@igxjs/node-components';
|
|
224
68
|
|
|
225
|
-
|
|
69
|
+
const jwt = new JwtManager({ expirationTime: '1h' });
|
|
70
|
+
const SECRET = process.env.JWT_SECRET;
|
|
226
71
|
|
|
227
|
-
|
|
72
|
+
// Create token
|
|
73
|
+
const token = await jwt.encrypt({ userId: '123', email: 'user@example.com' }, SECRET);
|
|
228
74
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
throw new CustomError(httpCodes.BAD_REQUEST, 'Invalid input data');
|
|
232
|
-
|
|
233
|
-
// With error details and additional data
|
|
234
|
-
throw new CustomError(
|
|
235
|
-
httpCodes.UNAUTHORIZED,
|
|
236
|
-
'Authentication failed',
|
|
237
|
-
{ originalError: err },
|
|
238
|
-
{ attemptedEmail: email }
|
|
239
|
-
);
|
|
75
|
+
// Verify token
|
|
76
|
+
const { payload } = await jwt.decrypt(token, SECRET);
|
|
240
77
|
```
|
|
241
78
|
|
|
242
|
-
|
|
79
|
+
[📖 Full JWT Manager Documentation](./docs/jwt-manager.md)
|
|
243
80
|
|
|
244
|
-
|
|
245
|
-
// Use httpHelper to handle Axios errors
|
|
246
|
-
try {
|
|
247
|
-
await axios.get('https://api.example.com/data');
|
|
248
|
-
} catch (error) {
|
|
249
|
-
// Convert Axios error to CustomError
|
|
250
|
-
throw httpHelper.handleAxiosError(error, 'Failed to fetch data');
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Or use it in error handling
|
|
254
|
-
app.get('/api/data', async (req, res, next) => {
|
|
255
|
-
try {
|
|
256
|
-
const response = await axios.get('https://external-api.com/data');
|
|
257
|
-
res.json(response.data);
|
|
258
|
-
} catch (error) {
|
|
259
|
-
return next(httpHelper.handleAxiosError(error, 'Failed to fetch external data'));
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
**Error Handler Middleware:**
|
|
81
|
+
### HTTP Handlers
|
|
265
82
|
|
|
266
83
|
```javascript
|
|
267
|
-
import
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
84
|
+
import {
|
|
85
|
+
httpCodes,
|
|
86
|
+
httpError,
|
|
87
|
+
httpErrorHandler,
|
|
88
|
+
httpNotFoundHandler
|
|
89
|
+
} from '@igxjs/node-components';
|
|
271
90
|
|
|
272
|
-
//
|
|
91
|
+
// Use in routes
|
|
273
92
|
app.get('/api/data', async (req, res, next) => {
|
|
274
93
|
try {
|
|
275
|
-
|
|
94
|
+
const data = await fetchData();
|
|
95
|
+
res.json(data);
|
|
276
96
|
} catch (error) {
|
|
277
|
-
next(error);
|
|
97
|
+
next(httpError(httpCodes.SYSTEM_FAILURE, 'Failed to fetch data', error));
|
|
278
98
|
}
|
|
279
99
|
});
|
|
280
100
|
|
|
281
|
-
// Add
|
|
101
|
+
// Add middleware
|
|
102
|
+
app.use(httpNotFoundHandler);
|
|
282
103
|
app.use(httpErrorHandler);
|
|
283
104
|
```
|
|
284
105
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
```javascript
|
|
288
|
-
httpCodes.OK // 200
|
|
289
|
-
httpCodes.CREATED // 201
|
|
290
|
-
httpCodes.NO_CONTENT // 204
|
|
291
|
-
httpCodes.BAD_REQUEST // 400
|
|
292
|
-
httpCodes.UNAUTHORIZED // 401
|
|
293
|
-
httpCodes.FORBIDDEN // 403
|
|
294
|
-
httpCodes.NOT_FOUND // 404
|
|
295
|
-
httpCodes.NOT_ACCEPTABLE // 406
|
|
296
|
-
httpCodes.CONFLICT // 409
|
|
297
|
-
httpCodes.SYSTEM_FAILURE // 500
|
|
298
|
-
httpCodes.NOT_IMPLEMENTED // 501
|
|
299
|
-
|
|
300
|
-
// Corresponding messages
|
|
301
|
-
httpMessages.OK // 'OK'
|
|
302
|
-
httpMessages.BAD_REQUEST // 'Bad Request'
|
|
303
|
-
// ... etc
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
#### CustomError API
|
|
307
|
-
|
|
308
|
-
**Constructor:**
|
|
309
|
-
```javascript
|
|
310
|
-
// new CustomError(code, message, error, data)
|
|
311
|
-
// - code: number - HTTP status code
|
|
312
|
-
// - message: string - Error message
|
|
313
|
-
// - error: object (optional) - Original error object
|
|
314
|
-
// - object (optional) - Additional error data
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
**Properties:**
|
|
318
|
-
- **`code`** - HTTP status code (number)
|
|
319
|
-
- **`message`** - Error message (string)
|
|
320
|
-
- **`error`** - Original error object (if provided)
|
|
321
|
-
- **`data`** - Additional error data (if provided)
|
|
322
|
-
|
|
323
|
-
#### httpHelper API
|
|
324
|
-
|
|
325
|
-
The `httpHelper` object provides utility methods for error handling:
|
|
326
|
-
|
|
327
|
-
**Methods:**
|
|
328
|
-
|
|
329
|
-
- **`httpHelper.handleAxiosError(error, defaultMessage)`** - Analyze and convert Axios/HTTP errors to CustomError
|
|
330
|
-
- `error` (Error | AxiosError) - The error object to analyze
|
|
331
|
-
- `defaultMessage` (string, optional) - Default message if error message cannot be extracted (default: 'An error occurred')
|
|
332
|
-
- Returns: `CustomError` instance with extracted status code, message, and data
|
|
333
|
-
|
|
334
|
-
- **`httpHelper.format(str, ...args)`** - Format a string with placeholders
|
|
335
|
-
- `str` (string) - String with `{0}`, `{1}`, etc. placeholders
|
|
336
|
-
- `...args` - Values to replace placeholders
|
|
337
|
-
- Returns: Formatted string
|
|
338
|
-
|
|
339
|
-
- **`httpHelper.toZodMessage(error)`** - Generate friendly Zod validation error message
|
|
340
|
-
- `error` (ZodError) - Zod validation error
|
|
341
|
-
- Returns: Formatted error message string
|
|
342
|
-
|
|
343
|
-
---
|
|
106
|
+
[📖 Full HTTP Handlers Documentation](./docs/http-handlers.md)
|
|
344
107
|
|
|
345
108
|
## Features
|
|
346
109
|
|
|
347
|
-
- ✅ **
|
|
348
|
-
- ✅ **
|
|
349
|
-
- ✅ **
|
|
350
|
-
- ✅ **
|
|
351
|
-
- ✅ **
|
|
352
|
-
- ✅ **
|
|
353
|
-
- ✅ **
|
|
110
|
+
- ✅ **SSO Integration** - Full SSO support with Redis or memory storage
|
|
111
|
+
- ✅ **JWT Security** - Encrypted JWT tokens using JWE (jose library)
|
|
112
|
+
- ✅ **Flexible Routing** - Easy mounting with context paths and middleware
|
|
113
|
+
- ✅ **Redis Support** - TLS/SSL and automatic reconnection
|
|
114
|
+
- ✅ **Error Handling** - Standardized HTTP responses
|
|
115
|
+
- ✅ **TypeScript** - Complete type definitions included
|
|
116
|
+
- ✅ **Production Ready** - Session locking, auto-reconnection, error handling
|
|
354
117
|
|
|
355
118
|
## Requirements
|
|
356
119
|
|
|
@@ -358,22 +121,31 @@ The `httpHelper` object provides utility methods for error handling:
|
|
|
358
121
|
- Express.js >= 4.x
|
|
359
122
|
- Redis (optional, for session storage)
|
|
360
123
|
|
|
361
|
-
## TypeScript
|
|
124
|
+
## TypeScript Support
|
|
362
125
|
|
|
363
|
-
This package includes TypeScript definitions
|
|
126
|
+
This package includes TypeScript definitions:
|
|
364
127
|
|
|
365
128
|
```typescript
|
|
366
129
|
import type {
|
|
367
|
-
SessionConfig,
|
|
368
130
|
SessionManager,
|
|
369
|
-
|
|
370
|
-
|
|
131
|
+
SessionConfig,
|
|
132
|
+
JwtManager,
|
|
371
133
|
FlexRouter,
|
|
372
134
|
RedisManager,
|
|
373
135
|
CustomError
|
|
374
136
|
} from '@igxjs/node-components';
|
|
375
137
|
```
|
|
376
138
|
|
|
139
|
+
## Documentation
|
|
140
|
+
|
|
141
|
+
📚 **[Complete Documentation](./docs/README.md)** - Detailed guides for all components
|
|
142
|
+
|
|
143
|
+
- [SessionManager Documentation](./docs/session-manager.md) - Comprehensive SSO session management guide
|
|
144
|
+
- [FlexRouter Documentation](./docs/flex-router.md) - Advanced routing patterns
|
|
145
|
+
- [RedisManager Documentation](./docs/redis-manager.md) - Redis connection management
|
|
146
|
+
- [JWT Manager Documentation](./docs/jwt-manager.md) - Token authentication guide
|
|
147
|
+
- [HTTP Handlers Documentation](./docs/http-handlers.md) - Error handling utilities
|
|
148
|
+
|
|
377
149
|
## License
|
|
378
150
|
|
|
379
|
-
[Apache 2.0](LICENSE
|
|
151
|
+
[Apache 2.0](LICENSE)
|
|
@@ -10,6 +10,7 @@ export const httpMessages = {
|
|
|
10
10
|
NOT_FOUND: 'Not Found',
|
|
11
11
|
NOT_ACCEPTABLE: 'Not Acceptable',
|
|
12
12
|
CONFLICT: 'Conflict',
|
|
13
|
+
LOCKED: 'Locked',
|
|
13
14
|
SYSTEM_FAILURE: 'System Error',
|
|
14
15
|
NOT_IMPLEMENTED: 'Not Implemented',
|
|
15
16
|
};
|
|
@@ -17,13 +18,14 @@ export const httpMessages = {
|
|
|
17
18
|
export const httpCodes = {
|
|
18
19
|
OK: 200,
|
|
19
20
|
CREATED: 201,
|
|
20
|
-
NO_CONTENT:
|
|
21
|
+
NO_CONTENT: 204,
|
|
21
22
|
BAD_REQUEST: 400,
|
|
22
23
|
UNAUTHORIZED: 401,
|
|
23
24
|
FORBIDDEN: 403,
|
|
24
25
|
NOT_FOUND: 404,
|
|
25
26
|
NOT_ACCEPTABLE: 406,
|
|
26
27
|
CONFLICT: 409,
|
|
28
|
+
LOCKED: 423,
|
|
27
29
|
SYSTEM_FAILURE: 500,
|
|
28
30
|
NOT_IMPLEMENTED: 501,
|
|
29
31
|
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { jwtDecrypt, EncryptJWT } from 'jose';
|
|
2
|
+
|
|
3
|
+
export class JwtManager {
|
|
4
|
+
/**
|
|
5
|
+
* Create a new JwtManager instance with configurable defaults
|
|
6
|
+
* @param {Object} options Configuration options
|
|
7
|
+
* @param {string} [options.algorithm='dir'] JWE algorithm (e.g., 'dir', 'A128KW', 'A192KW', 'A256KW')
|
|
8
|
+
* @param {string} [options.encryption='A256GCM'] JWE encryption method (e.g., 'A256GCM', 'A128GCM', 'A192GCM')
|
|
9
|
+
* @param {string} [options.expirationTime='10m'] Token expiration time (e.g., '10m', '1h', '7d')
|
|
10
|
+
* @param {number} [options.clockTolerance=30] Clock tolerance in seconds for token validation
|
|
11
|
+
* @param {string} [options.secretHashAlgorithm='SHA-256'] Hash algorithm for secret derivation
|
|
12
|
+
* @param {string} [options.issuer] Optional JWT issuer claim
|
|
13
|
+
* @param {string} [options.audience] Optional JWT audience claim
|
|
14
|
+
* @param {string} [options.subject] Optional JWT subject claim
|
|
15
|
+
*/
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.algorithm = options.algorithm || 'dir';
|
|
18
|
+
this.encryption = options.encryption || 'A256GCM';
|
|
19
|
+
this.expirationTime = options.expirationTime || '10m';
|
|
20
|
+
this.clockTolerance = options.clockTolerance ?? 30;
|
|
21
|
+
this.secretHashAlgorithm = options.secretHashAlgorithm || 'SHA-256';
|
|
22
|
+
this.issuer = options.issuer;
|
|
23
|
+
this.audience = options.audience;
|
|
24
|
+
this.subject = options.subject;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generate JWT token for user session
|
|
29
|
+
* @param {import('jose').JWTPayload} data User data payload
|
|
30
|
+
* @param {string} secret Secret key or password for encryption
|
|
31
|
+
* @param {Object} [options] Per-call configuration overrides
|
|
32
|
+
* @param {string} [options.algorithm] Override default algorithm
|
|
33
|
+
* @param {string} [options.encryption] Override default encryption method
|
|
34
|
+
* @param {string} [options.expirationTime] Override default expiration time
|
|
35
|
+
* @param {string} [options.secretHashAlgorithm] Override default hash algorithm
|
|
36
|
+
* @param {string} [options.issuer] Override default issuer claim
|
|
37
|
+
* @param {string} [options.audience] Override default audience claim
|
|
38
|
+
* @param {string} [options.subject] Override default subject claim
|
|
39
|
+
* @returns {Promise<string>} Returns encrypted JWT token
|
|
40
|
+
*/
|
|
41
|
+
async encrypt(data, secret, options = {}) {
|
|
42
|
+
const algorithm = options.algorithm || this.algorithm;
|
|
43
|
+
const encryption = options.encryption || this.encryption;
|
|
44
|
+
const expirationTime = options.expirationTime || this.expirationTime;
|
|
45
|
+
const secretHashAlgorithm = options.secretHashAlgorithm || this.secretHashAlgorithm;
|
|
46
|
+
const issuer = options.issuer || this.issuer;
|
|
47
|
+
const audience = options.audience || this.audience;
|
|
48
|
+
const subject = options.subject || this.subject;
|
|
49
|
+
|
|
50
|
+
const secretHash = await crypto.subtle.digest(
|
|
51
|
+
secretHashAlgorithm,
|
|
52
|
+
new TextEncoder().encode(secret)
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const jwt = new EncryptJWT(data)
|
|
56
|
+
.setProtectedHeader({
|
|
57
|
+
alg: algorithm,
|
|
58
|
+
enc: encryption
|
|
59
|
+
})
|
|
60
|
+
.setIssuedAt()
|
|
61
|
+
.setExpirationTime(expirationTime);
|
|
62
|
+
|
|
63
|
+
// Add optional claims if provided
|
|
64
|
+
if (issuer) jwt.setIssuer(issuer);
|
|
65
|
+
if (audience) jwt.setAudience(audience);
|
|
66
|
+
if (subject) jwt.setSubject(subject);
|
|
67
|
+
|
|
68
|
+
return await jwt.encrypt(new Uint8Array(secretHash));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Decrypt JWT token for user session
|
|
73
|
+
* @param {string} token JWT token to decrypt
|
|
74
|
+
* @param {string} secret Secret key or password for decryption
|
|
75
|
+
* @param {Object} [options] Per-call configuration overrides
|
|
76
|
+
* @param {number} [options.clockTolerance] Override default clock tolerance
|
|
77
|
+
* @param {string} [options.secretHashAlgorithm] Override default hash algorithm
|
|
78
|
+
* @param {string} [options.issuer] Expected issuer claim for validation
|
|
79
|
+
* @param {string} [options.audience] Expected audience claim for validation
|
|
80
|
+
* @param {string} [options.subject] Expected subject claim for validation
|
|
81
|
+
* @returns {Promise<import('jose').JWTDecryptResult<import('jose').EncryptJWT>>} Returns decrypted JWT token
|
|
82
|
+
*/
|
|
83
|
+
async decrypt(token, secret, options = {}) {
|
|
84
|
+
const clockTolerance = options.clockTolerance ?? this.clockTolerance;
|
|
85
|
+
const secretHashAlgorithm = options.secretHashAlgorithm || this.secretHashAlgorithm;
|
|
86
|
+
const issuer = options.issuer || this.issuer;
|
|
87
|
+
const audience = options.audience || this.audience;
|
|
88
|
+
const subject = options.subject || this.subject;
|
|
89
|
+
|
|
90
|
+
const secretHash = await crypto.subtle.digest(
|
|
91
|
+
secretHashAlgorithm,
|
|
92
|
+
new TextEncoder().encode(secret)
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const decryptOptions = { clockTolerance };
|
|
96
|
+
|
|
97
|
+
// Add optional claim validations if provided
|
|
98
|
+
if (issuer) decryptOptions.issuer = issuer;
|
|
99
|
+
if (audience) decryptOptions.audience = audience;
|
|
100
|
+
if (subject) decryptOptions.subject = subject;
|
|
101
|
+
|
|
102
|
+
return await jwtDecrypt(token, new Uint8Array(secretHash), decryptOptions);
|
|
103
|
+
}
|
|
104
|
+
}
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Node Components Documentation
|
|
2
|
+
|
|
3
|
+
Detailed documentation for `@igxjs/node-components` - shared components for Express.js applications.
|
|
4
|
+
|
|
5
|
+
## 📚 Component Documentation
|
|
6
|
+
|
|
7
|
+
### Core Modules
|
|
8
|
+
|
|
9
|
+
- **[SessionManager](./session-manager.md)** - SSO session management with Redis and memory storage
|
|
10
|
+
- Configuration options and singleton pattern
|
|
11
|
+
- Complete setup and usage examples
|
|
12
|
+
- API reference for all methods
|
|
13
|
+
|
|
14
|
+
- **[FlexRouter](./flex-router.md)** - Flexible routing utility for Express.js
|
|
15
|
+
- Context path and middleware management
|
|
16
|
+
- API versioning and route organization
|
|
17
|
+
- Advanced usage patterns
|
|
18
|
+
|
|
19
|
+
- **[RedisManager](./redis-manager.md)** - Redis connection management
|
|
20
|
+
- TLS/SSL support
|
|
21
|
+
- Connection monitoring and error handling
|
|
22
|
+
- Direct Redis operations
|
|
23
|
+
|
|
24
|
+
- **[JWT Manager](./jwt-manager.md)** - JWT encryption and decryption
|
|
25
|
+
- Secure token-based authentication
|
|
26
|
+
- Express.js integration patterns
|
|
27
|
+
- Refresh token implementation
|
|
28
|
+
|
|
29
|
+
- **[HTTP Handlers](./http-handlers.md)** - Standardized error handling
|
|
30
|
+
- Custom error classes and middleware
|
|
31
|
+
- HTTP status codes and messages
|
|
32
|
+
- Axios error handling and validation helpers
|
|
33
|
+
|
|
34
|
+
## 🚀 Quick Links
|
|
35
|
+
|
|
36
|
+
- [Main README](../README.md) - Package overview and installation
|
|
37
|
+
- [GitHub Repository](https://github.com/igxjs/node-components)
|
|
38
|
+
|
|
39
|
+
## 💡 Getting Help
|
|
40
|
+
|
|
41
|
+
If you need help:
|
|
42
|
+
1. Check the relevant component documentation above
|
|
43
|
+
2. Review the examples in each guide
|
|
44
|
+
3. Look at the type definitions in `index.d.ts`
|
|
45
|
+
4. Open an issue on GitHub
|
|
46
|
+
|
|
47
|
+
## 📖 Documentation Structure
|
|
48
|
+
|
|
49
|
+
Each component documentation includes:
|
|
50
|
+
- Overview of features
|
|
51
|
+
- Configuration options
|
|
52
|
+
- Basic and advanced usage examples
|
|
53
|
+
- Complete API reference
|
|
54
|
+
- Related documentation links
|