@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.
@@ -0,0 +1,210 @@
1
+ # RedisManager
2
+
3
+ Redis connection management with TLS support and automatic reconnection handling.
4
+
5
+ ## Overview
6
+
7
+ RedisManager provides a robust Redis client with:
8
+ - TLS/SSL support for secure connections
9
+ - Automatic reconnection handling
10
+ - Connection status monitoring
11
+ - Clean disconnection management
12
+
13
+ **Note:** RedisManager is used internally by the [SessionManager](./session-manager.md), so you typically don't need to use it directly unless you need custom Redis operations.
14
+
15
+ ## Usage Example
16
+
17
+ ```javascript
18
+ import { RedisManager } from '@igxjs/node-components';
19
+
20
+ const redisManager = new RedisManager();
21
+
22
+ // Connect to Redis (with optional TLS certificate)
23
+ const connected = await redisManager.connect(
24
+ 'rediss://localhost:6379',
25
+ '/path/to/cert.pem'
26
+ );
27
+
28
+ if (connected) {
29
+ // Get Redis client for direct operations
30
+ const client = redisManager.getClient();
31
+ await client.set('key', 'value');
32
+ const value = await client.get('key');
33
+ console.log(value); // 'value'
34
+
35
+ // Check connection status
36
+ const isConnected = await redisManager.isConnected();
37
+ console.log('Connected:', isConnected);
38
+
39
+ // Disconnect when done
40
+ await redisManager.disConnect();
41
+ }
42
+ ```
43
+
44
+ ## Connection URLs
45
+
46
+ RedisManager supports two URL formats:
47
+
48
+ ### Standard Redis (non-TLS)
49
+ ```javascript
50
+ await redisManager.connect('redis://localhost:6379');
51
+ await redisManager.connect('redis://username:password@host:6379');
52
+ ```
53
+
54
+ ### Secure Redis (TLS)
55
+ ```javascript
56
+ // Requires certificate path for TLS
57
+ await redisManager.connect(
58
+ 'rediss://localhost:6379',
59
+ '/path/to/certificate.pem'
60
+ );
61
+ ```
62
+
63
+ ## Advanced Usage
64
+
65
+ ### Custom Operations
66
+
67
+ ```javascript
68
+ import { RedisManager } from '@igxjs/node-components';
69
+
70
+ const redisManager = new RedisManager();
71
+ await redisManager.connect(process.env.REDIS_URL);
72
+
73
+ const client = redisManager.getClient();
74
+
75
+ // String operations
76
+ await client.set('user:1', JSON.stringify({ name: 'John' }));
77
+ const user = JSON.parse(await client.get('user:1'));
78
+
79
+ // Hash operations
80
+ await client.hSet('user:2', 'name', 'Jane');
81
+ await client.hSet('user:2', 'email', 'jane@example.com');
82
+ const userData = await client.hGetAll('user:2');
83
+
84
+ // List operations
85
+ await client.lPush('tasks', 'task1');
86
+ await client.lPush('tasks', 'task2');
87
+ const tasks = await client.lRange('tasks', 0, -1);
88
+
89
+ // Set operations
90
+ await client.sAdd('tags', 'javascript');
91
+ await client.sAdd('tags', 'nodejs');
92
+ const tags = await client.sMembers('tags');
93
+
94
+ // Expiration
95
+ await client.setEx('temporary', 3600, 'expires in 1 hour');
96
+ await client.expire('user:1', 3600);
97
+
98
+ // Clean up
99
+ await redisManager.disConnect();
100
+ ```
101
+
102
+ ### Connection Monitoring
103
+
104
+ ```javascript
105
+ const redisManager = new RedisManager();
106
+
107
+ // Check if connected before operations
108
+ if (await redisManager.isConnected()) {
109
+ const client = redisManager.getClient();
110
+ // Perform operations
111
+ } else {
112
+ console.log('Redis not connected');
113
+ }
114
+ ```
115
+
116
+ ### Error Handling
117
+
118
+ ```javascript
119
+ const redisManager = new RedisManager();
120
+
121
+ try {
122
+ const connected = await redisManager.connect(process.env.REDIS_URL);
123
+
124
+ if (!connected) {
125
+ console.error('Failed to connect to Redis');
126
+ // Fall back to memory storage or handle appropriately
127
+ return;
128
+ }
129
+
130
+ const client = redisManager.getClient();
131
+ await client.set('key', 'value');
132
+
133
+ } catch (error) {
134
+ console.error('Redis operation failed:', error);
135
+ } finally {
136
+ await redisManager.disConnect();
137
+ }
138
+ ```
139
+
140
+ ## API Methods
141
+
142
+ ### `connect(redisUrl, certPath?)`
143
+
144
+ Connect to Redis server.
145
+
146
+ **Parameters:**
147
+ - `redisUrl` (string) - Redis connection URL (`redis://` or `rediss://`)
148
+ - `certPath` (string, optional) - Path to TLS certificate file (required for `rediss://`)
149
+
150
+ **Returns:** `Promise<boolean>` - Returns `true` if connected successfully
151
+
152
+ **Example:**
153
+ ```javascript
154
+ // Non-TLS connection
155
+ await redisManager.connect('redis://localhost:6379');
156
+
157
+ // TLS connection
158
+ await redisManager.connect('rediss://localhost:6379', '/path/to/cert.pem');
159
+ ```
160
+
161
+ ### `getClient()`
162
+
163
+ Get the Redis client instance for direct operations.
164
+
165
+ **Returns:** Redis client object
166
+
167
+ **Example:**
168
+ ```javascript
169
+ const client = redisManager.getClient();
170
+ await client.set('key', 'value');
171
+ await client.get('key');
172
+ ```
173
+
174
+ ### `isConnected()`
175
+
176
+ Check if Redis connection is active and responsive.
177
+
178
+ **Returns:** `Promise<boolean>` - Returns `true` if connected and responsive
179
+
180
+ **Example:**
181
+ ```javascript
182
+ const connected = await redisManager.isConnected();
183
+ if (connected) {
184
+ // Proceed with operations
185
+ }
186
+ ```
187
+
188
+ ### `disConnect()`
189
+
190
+ Disconnect from Redis server and clean up resources.
191
+
192
+ **Returns:** `Promise<void>`
193
+
194
+ **Example:**
195
+ ```javascript
196
+ await redisManager.disConnect();
197
+ ```
198
+
199
+ ## Features
200
+
201
+ - **Automatic Reconnection**: Handles connection drops and reconnects automatically
202
+ - **TLS Support**: Secure connections with certificate-based authentication
203
+ - **Connection Pooling**: Efficient connection management
204
+ - **Error Handling**: Graceful error handling and logging
205
+ - **Health Checks**: Built-in connection status monitoring
206
+
207
+ ## Related Documentation
208
+
209
+ - [SessionManager](./session-manager.md) - Uses RedisManager for session storage
210
+ - [Back to main documentation](../README.md)
@@ -0,0 +1,160 @@
1
+ # SessionManager
2
+
3
+ Provides SSO (Single Sign-On) session management with support for Redis and memory-based session stores.
4
+
5
+ ## Configuration Options
6
+
7
+ ```javascript
8
+ // Example configuration object
9
+ // All fields are strings except SESSION_AGE which is a number
10
+
11
+ const config = {
12
+ // SSO Configuration
13
+ SSO_ENDPOINT_URL: 'https://sso.example.com',
14
+ SSO_CLIENT_ID: 'your-client-id',
15
+ SSO_CLIENT_SECRET: 'your-client-secret',
16
+ SSO_SUCCESS_URL: '/dashboard',
17
+ SSO_FAILURE_URL: '/login',
18
+
19
+ // Session Configuration
20
+ SESSION_AGE: 64800000, // 18 hours in milliseconds
21
+ SESSION_COOKIE_PATH: '/',
22
+ SESSION_SECRET: 'your-session-secret',
23
+ SESSION_PREFIX: 'ibmid:', // Default value when not provided
24
+
25
+ // Redis Configuration (optional - uses memory store if not provided)
26
+ REDIS_URL: 'redis://localhost:6379',
27
+ REDIS_CERT_PATH: '/path/to/cert.pem' // For TLS connections
28
+ };
29
+ ```
30
+
31
+ ## ⚠️ Important: Singleton Pattern
32
+
33
+ **SessionManager should be instantiated once and exported as a singleton.**
34
+
35
+ **Why?**
36
+ - SessionManager manages Redis connections (if configured)
37
+ - Multiple instances can lead to connection pool exhaustion
38
+ - Session state consistency requires a single source of truth
39
+ - Middleware functions need to reference the same instance
40
+
41
+ **✅ Recommended:** Create a separate module that exports a single SessionManager instance
42
+ **❌ Avoid:** Creating new SessionManager instances in multiple files
43
+
44
+ ## Recommended File Structure
45
+
46
+ ```
47
+ your-project/
48
+ ├── src/
49
+ │ ├── config/
50
+ │ │ └── session-manager.js ← Create SessionManager singleton here
51
+ │ ├── routes/
52
+ │ │ └── auth.js ← Import session from config
53
+ │ └── app.js ← Import and setup session
54
+ └── package.json
55
+ ```
56
+
57
+ ## Usage Example
58
+
59
+ ### Step 1: Create SessionManager Singleton
60
+
61
+ Create a dedicated file for your SessionManager instance:
62
+
63
+ ```javascript
64
+ // config/session-manager.js
65
+ import { SessionManager } from '@igxjs/node-components';
66
+
67
+ // Create and export a single SessionManager instance
68
+ export const session = new SessionManager({
69
+ SSO_ENDPOINT_URL: process.env.SSO_ENDPOINT_URL,
70
+ SSO_CLIENT_ID: process.env.SSO_CLIENT_ID,
71
+ SSO_CLIENT_SECRET: process.env.SSO_CLIENT_SECRET,
72
+ SSO_SUCCESS_URL: '/dashboard',
73
+ SSO_FAILURE_URL: '/login',
74
+ SESSION_AGE: 64800000, // 18 hours in milliseconds
75
+ SESSION_COOKIE_PATH: '/',
76
+ SESSION_SECRET: process.env.SESSION_SECRET,
77
+ SESSION_PREFIX: 'ibmid:',
78
+ REDIS_URL: process.env.REDIS_URL,
79
+ REDIS_CERT_PATH: process.env.REDIS_CERT_PATH
80
+ });
81
+ ```
82
+
83
+ ### Step 2: Setup and Use in Your Application
84
+
85
+ Import and use the singleton instance throughout your application:
86
+
87
+ ```javascript
88
+ // app.js or index.js
89
+ import express from 'express';
90
+ import { session } from './config/session-manager.js';
91
+
92
+ const app = express();
93
+
94
+ // Initialize session middleware (call once during app startup)
95
+ await session.setup(app, (user) => {
96
+ // Process user object - compute permissions, avatar URL, etc.
97
+ return {
98
+ ...user,
99
+ displayName: user.email?.split('@')[0],
100
+ hasAdminAccess: user.authorized && user.email?.endsWith('@admin.com')
101
+ };
102
+ });
103
+
104
+ // Use session instance in your routes
105
+ app.get('/protected', session.authenticate(), (req, res) => {
106
+ res.json({ user: req.user });
107
+ });
108
+
109
+ app.get('/auth/callback', session.callback((user) => {
110
+ return {
111
+ ...user,
112
+ loginTime: new Date()
113
+ };
114
+ }));
115
+
116
+ app.get('/auth/providers', session.identityProviders());
117
+ app.post('/auth/refresh', session.refresh((user) => {
118
+ return { ...user, refreshedAt: new Date() };
119
+ }));
120
+ app.get('/auth/logout', session.logout());
121
+
122
+ app.listen(3000, () => {
123
+ console.log('Server running on port 3000');
124
+ });
125
+ ```
126
+
127
+ ### Step 3: Import in Other Files
128
+
129
+ ```javascript
130
+ // routes/auth.js
131
+ import { Router } from 'express';
132
+ import { session } from '../config/session-manager.js';
133
+
134
+ const router = Router();
135
+
136
+ // Reuse the same session instance
137
+ router.get('/profile', session.authenticate(), (req, res) => {
138
+ res.json({ profile: req.user });
139
+ });
140
+
141
+ export default router;
142
+ ```
143
+
144
+ ## API Methods
145
+
146
+ - **`setup(app, updateUser)`** - Initialize session configurations
147
+ - **`authenticate(isDebugging?, redirectUrl?)`** - Resource protection middleware
148
+ - **`callback(initUser)`** - SSO callback handler for successful login
149
+ - **`identityProviders()`** - Get available identity providers
150
+ - **`logout()`** - Application logout handler (not SSO logout)
151
+ - **`refresh(initUser)`** - Refresh user session with new token
152
+ - **`redisManager()`** - Get the RedisManager instance (returns RedisManager or null)
153
+ - **`hasLock(email)`** - Check if email has a session refresh lock
154
+ - **`lock(email)`** - Lock email for session refresh (prevents concurrent refreshes)
155
+ - **`clearLocks()`** - Clear expired session refresh locks
156
+
157
+ ## Related Documentation
158
+
159
+ - [RedisManager](./redis-manager.md) - Used internally by SessionManager for Redis storage
160
+ - [Back to main documentation](../README.md)
package/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import 'express-session';
2
2
 
3
3
  import { AxiosError } from 'axios';
4
+ import { EncryptJWT, JWTDecryptResult } from 'jose';
4
5
  import { RedisClientType } from '@redis/client';
5
6
  import { Application, RequestHandler, Request, Response, NextFunction, Router } from 'express';
6
7
 
@@ -197,6 +198,93 @@ export class RedisManager {
197
198
  disConnect(): Promise<void>;
198
199
  }
199
200
 
201
+ // JWT Manager Configuration
202
+ export interface JwtManagerOptions {
203
+ /** @type {string} JWE algorithm (default: 'dir') */
204
+ algorithm?: string;
205
+ /** @type {string} JWE encryption method (default: 'A256GCM') */
206
+ encryption?: string;
207
+ /** @type {string} Token expiration time (default: '10m') */
208
+ expirationTime?: string;
209
+ /** @type {number} Clock tolerance in seconds for token validation (default: 30) */
210
+ clockTolerance?: number;
211
+ /** @type {string} Hash algorithm for secret derivation (default: 'SHA-256') */
212
+ secretHashAlgorithm?: string;
213
+ /** @type {string} Optional JWT issuer claim */
214
+ issuer?: string;
215
+ /** @type {string} Optional JWT audience claim */
216
+ audience?: string;
217
+ /** @type {string} Optional JWT subject claim */
218
+ subject?: string;
219
+ }
220
+
221
+ export interface JwtEncryptOptions {
222
+ /** @type {string} Override default algorithm */
223
+ algorithm?: string;
224
+ /** @type {string} Override default encryption method */
225
+ encryption?: string;
226
+ /** @type {string} Override default expiration time */
227
+ expirationTime?: string;
228
+ /** @type {string} Override default hash algorithm */
229
+ secretHashAlgorithm?: string;
230
+ /** @type {string} Override default issuer claim */
231
+ issuer?: string;
232
+ /** @type {string} Override default audience claim */
233
+ audience?: string;
234
+ /** @type {string} Override default subject claim */
235
+ subject?: string;
236
+ }
237
+
238
+ export interface JwtDecryptOptions {
239
+ /** @type {number} Override default clock tolerance */
240
+ clockTolerance?: number;
241
+ /** @type {string} Override default hash algorithm */
242
+ secretHashAlgorithm?: string;
243
+ /** @type {string} Expected issuer claim for validation */
244
+ issuer?: string;
245
+ /** @type {string} Expected audience claim for validation */
246
+ audience?: string;
247
+ /** @type {string} Expected subject claim for validation */
248
+ subject?: string;
249
+ }
250
+
251
+ export type DecryptedJWT = JWTDecryptResult<EncryptJWT>;
252
+ // JwtManager class for JWT encryption and decryption
253
+ export class JwtManager {
254
+ algorithm: string;
255
+ encryption: string;
256
+ expirationTime: string;
257
+ clockTolerance: number;
258
+ secretHashAlgorithm: string;
259
+ issuer?: string;
260
+ audience?: string;
261
+ subject?: string;
262
+
263
+ /**
264
+ * Create a new JwtManager instance with configurable defaults
265
+ * @param options Configuration options
266
+ */
267
+ constructor(options?: JwtManagerOptions);
268
+
269
+ /**
270
+ * Generate JWT token for user session
271
+ * @param data User data payload
272
+ * @param input Secret key or password for encryption
273
+ * @param options Per-call configuration overrides
274
+ * @returns Returns encrypted JWT token
275
+ */
276
+ encrypt( JWTPayload, input: string, options?: JwtEncryptOptions): Promise<string>;
277
+
278
+ /**
279
+ * Decrypt JWT token for user session
280
+ * @param token JWT token to decrypt
281
+ * @param input Secret key or password for decryption
282
+ * @param options Per-call configuration overrides
283
+ * @returns Returns decrypted JWT token
284
+ */
285
+ decrypt(token: string, input: string, options?: JwtDecryptOptions): Promise<DecryptedJWT>;
286
+ }
287
+
200
288
  // HTTP status code keys (exposed for type safety)
201
289
  export const httpCodes: {
202
290
  OK: number;
@@ -208,6 +296,7 @@ export const httpCodes: {
208
296
  NOT_FOUND: number;
209
297
  NOT_ACCEPTABLE: number;
210
298
  CONFLICT: number;
299
+ LOCKED: number;
211
300
  SYSTEM_FAILURE: number;
212
301
  NOT_IMPLEMENTED: number;
213
302
  };
@@ -223,6 +312,7 @@ export const httpMessages: {
223
312
  NOT_FOUND: string;
224
313
  NOT_ACCEPTABLE: string;
225
314
  CONFLICT: string;
315
+ LOCKED: string;
226
316
  SYSTEM_FAILURE: string;
227
317
  NOT_IMPLEMENTED: string;
228
318
  };
@@ -255,6 +345,21 @@ export const httpHelper: {
255
345
  handleAxiosError(error: Error | AxiosError, defaultMessage?: string): CustomError;
256
346
  };
257
347
 
348
+ /**
349
+ * HTTP Error - alias of `new CustomError()`
350
+ * @param code Error code
351
+ * @param message Error message
352
+ * @param error Original Error instance
353
+ * @param data Error data
354
+ * @returns Returns a new instance of CustomError
355
+ */
356
+ export function httpError(
357
+ code: number,
358
+ message: string,
359
+ error?: object,
360
+ data?: object
361
+ ): CustomError;
362
+
258
363
  /**
259
364
  * Custom error handler middleware
260
365
  * @param err Error object
package/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { SessionConfig, SessionManager } from './components/session.js';
2
- export { httpCodes, httpMessages, httpErrorHandler, CustomError, httpHelper } from './components/http-handlers.js';
2
+ export { httpCodes, httpMessages, httpErrorHandler, httpNotFoundHandler, CustomError, httpHelper, httpError } from './components/http-handlers.js';
3
3
  export { RedisManager } from './components/redis.js';
4
4
  export { FlexRouter } from './components/router.js';
5
+ export { JwtManager } from './components/jwt.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@igxjs/node-components",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Node components for igxjs",
5
5
  "main": "index.js",
6
6
  "type": "module",