@igxjs/node-components 1.0.9 → 1.0.11

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 CHANGED
@@ -12,7 +12,7 @@ npm install @igxjs/node-components
12
12
 
13
13
  | Component | Description | Documentation |
14
14
  |-----------|-------------|---------------|
15
- | **SessionManager** | SSO session management with Redis/memory storage | [View docs](./docs/session-manager.md) |
15
+ | **SessionManager** | SSO session management with Redis/memory storage, supporting both session and token-based authentication | [View docs](./docs/session-manager.md) |
16
16
  | **FlexRouter** | Flexible routing with context paths and middleware | [View docs](./docs/flex-router.md) |
17
17
  | **RedisManager** | Redis connection management with TLS support | [View docs](./docs/redis-manager.md) |
18
18
  | **JWT Manager** | Secure JWT encryption/decryption with JWE | [View docs](./docs/jwt-manager.md) |
@@ -23,9 +23,9 @@ npm install @igxjs/node-components
23
23
  ### SessionManager
24
24
 
25
25
  ```javascript
26
- import { SessionManager } from '@igxjs/node-components';
26
+ import { SessionManager, SessionMode } from '@igxjs/node-components';
27
27
 
28
- // Create singleton instance
28
+ // Create singleton instance with SESSION authentication (default)
29
29
  export const session = new SessionManager({
30
30
  SSO_ENDPOINT_URL: process.env.SSO_ENDPOINT_URL,
31
31
  SSO_CLIENT_ID: process.env.SSO_CLIENT_ID,
@@ -34,6 +34,16 @@ export const session = new SessionManager({
34
34
  REDIS_URL: process.env.REDIS_URL
35
35
  });
36
36
 
37
+ // Create singleton instance with TOKEN authentication
38
+ export const tokenSession = new SessionManager({
39
+ SESSION_MODE: SessionMode.TOKEN, // Use token-based authentication
40
+ SSO_ENDPOINT_URL: process.env.SSO_ENDPOINT_URL,
41
+ SSO_CLIENT_ID: process.env.SSO_CLIENT_ID,
42
+ SSO_CLIENT_SECRET: process.env.SSO_CLIENT_SECRET,
43
+ SESSION_SECRET: process.env.SESSION_SECRET,
44
+ REDIS_URL: process.env.REDIS_URL,
45
+ });
46
+
37
47
  // Setup in your app
38
48
  await session.setup(app, (user) => ({ ...user, displayName: user.email }));
39
49
 
@@ -105,9 +115,93 @@ app.use(httpErrorHandler);
105
115
 
106
116
  [📖 Full HTTP Handlers Documentation](./docs/http-handlers.md)
107
117
 
118
+ ## SessionManager Authentication Modes
119
+
120
+ The `SessionManager` supports two authentication modes:
121
+
122
+ ### SESSION Mode (Default)
123
+
124
+ Uses traditional server-side session cookies. When a user authenticates via SSO, their session is stored in Redis or memory storage. The client sends the session cookie with each request to prove authentication.
125
+
126
+ **Configuration:**
127
+ - `SESSION_MODE`: `SessionMode.SESSION` (default) - Uses session-based authentication
128
+ - `SESSION_AGE`: Session timeout in milliseconds (default: 64800000)
129
+ - `REDIS_URL`: Redis connection string for session storage
130
+
131
+ **Auth Methods:**
132
+ - `session.authenticate()` - Protect routes with SSO session verification
133
+ - `session.verifySession(isDebugging, redirectUrl)` - Explicit session verification method
134
+ - `session.logout(redirect?, all?)` - Logout current session (or logout all for token mode)
135
+
136
+ ### TOKEN Mode
137
+
138
+ Uses JWT bearer tokens instead of session cookies. When a user authenticates via SSO, a JWT token is generated and stored in Redis. The client includes the token in the Authorization header (`Bearer {token}`) with each request.
139
+
140
+ **Configuration:**
141
+ - `SESSION_MODE`: `SessionMode.TOKEN` - Uses token-based authentication
142
+ - `SSO_SUCCESS_URL`: Redirect URL after successful SSO login
143
+ - `SSO_FAILURE_URL`: Redirect URL after failed SSO login
144
+ - `JWT_ALGORITHM`: JWT algorithm (default: `'dir'`)
145
+ - `JWT_ENCRYPTION`: Encryption algorithm (default: `'A256GCM'`)
146
+ - `JWT_EXPIRATION_TIME`: Token expiration time (default: `'10m'`)
147
+ - `JWT_CLOCK_TOLERANCE`: Clock skew tolerance in seconds (default: 30)
148
+
149
+ **Auth Methods:**
150
+ - `session.verifyToken(isDebugging, redirectUrl)` - Protect routes with token verification
151
+ - `session.callback(initUser)` - SSO callback handler for token generation
152
+ - `session.refresh(initUser)` - Refresh user authentication based on auth mode
153
+ - `session.logout(redirect?, all?)` - Logout current or all tokens
154
+
155
+ **Token Storage (Client-Side):**
156
+
157
+ When using token-based authentication, the client-side HTML page stores the token in `localStorage`:
158
+
159
+ ```html
160
+ <script>
161
+ // Store auth data in localStorage
162
+ localStorage.setItem('authToken', ${JSON.stringify(token)});
163
+ localStorage.setItem('tokenExpiry', ${Date.now() + sessionAge});
164
+ localStorage.setItem('user', ${JSON.stringify({
165
+ email: user.email,
166
+ name: user.name,
167
+ })});
168
+
169
+ // Redirect to original destination
170
+ window.location.replace(redirectUrl);
171
+ </script>
172
+ ```
173
+
174
+ ## SessionManager Configuration Options
175
+
176
+ | Option | Type | Default | Description |
177
+ |--------|------|---------|-------------|
178
+ | `SSO_ENDPOINT_URL` | string | - | Identity provider endpoint URL |
179
+ | `SSO_CLIENT_ID` | string | - | SSO client ID |
180
+ | `SSO_CLIENT_SECRET` | string | - | SSO client secret |
181
+ | `SSO_SUCCESS_URL` | string | - | Redirect URL after successful login (token mode) |
182
+ | `SSO_FAILURE_URL` | string | - | Redirect URL after failed login (token mode) |
183
+ | `SESSION_MODE` | string | `SessionMode.SESSION` | Authentication mode: `SessionMode.SESSION` or `SessionMode.TOKEN` |
184
+ | `SESSION_AGE` | number | 64800000 | Session timeout in milliseconds |
185
+ | `SESSION_COOKIE_PATH` | string | `'/'` | Session cookie path |
186
+ | `SESSION_SECRET` | string | - | Session/JWT secret key |
187
+ | `SESSION_PREFIX` | string | `'ibmid:'` | Redis session/key prefix |
188
+ | `REDIS_URL` | string | - | Redis connection URL (optional) |
189
+ | `REDIS_CERT_PATH` | string | - | Path to Redis TLS certificate |
190
+ | `JWT_ALGORITHM` | string | `'dir'` | JWT signing algorithm |
191
+ | `JWT_ENCRYPTION` | string | `'A256GCM'` | JWE encryption algorithm |
192
+ | `JWT_EXPIRATION_TIME` | string | `'10m'` | Token expiration duration |
193
+ | `JWT_CLOCK_TOLERANCE` | number | 30 | Clock skew tolerance in seconds |
194
+ | `JWT_SECRET_HASH_ALGORITHM` | string | `'SHA-256'` | Algorithm for hashing secrets |
195
+ | `JWT_ISSUER` | string | - | JWT issuer identifier |
196
+ | `JWT_AUDIENCE` | string | - | JWT audience identifier |
197
+ | `JWT_SUBJECT` | string | - | JWT subject identifier |
198
+
108
199
  ## Features
109
200
 
110
201
  - ✅ **SSO Integration** - Full SSO support with Redis or memory storage
202
+ - ✅ **Dual Authentication Modes** - SESSION (cookies) or TOKEN (Bearer tokens)
203
+ - ✅ **Token Refresh** - Automatic token refresh via SSO endpoints
204
+ - ✅ **Session Refresh Locks** - Prevent concurrent token/session refresh attacks
111
205
  - ✅ **JWT Security** - Encrypted JWT tokens using JWE (jose library)
112
206
  - ✅ **Flexible Routing** - Easy mounting with context paths and middleware
113
207
  - ✅ **Redis Support** - TLS/SSL and automatic reconnection
@@ -148,4 +242,4 @@ import type {
148
242
 
149
243
  ## License
150
244
 
151
- [Apache 2.0](LICENSE)
245
+ [Apache 2.0](LICENSE)
@@ -141,6 +141,12 @@ const _getErrorMessage = (error, defaultMessage) => {
141
141
  };
142
142
 
143
143
  export const httpHelper = {
144
+ /**
145
+ * Format a string with placeholders
146
+ * @param {string} str String with {0}, {1}, etc. placeholders
147
+ * @param {...string} args Values to replace placeholders
148
+ * @returns {string} Formatted string
149
+ */
144
150
  format (str, ...args) {
145
151
  const matched = str.match(/{\d}/ig);
146
152
  matched.forEach((element, index) => {
package/components/jwt.js CHANGED
@@ -1,51 +1,89 @@
1
+ import crypto from 'node:crypto';
2
+
1
3
  import { jwtDecrypt, EncryptJWT } from 'jose';
2
4
 
5
+ /**
6
+ * JwtManager configuration options
7
+ * Uses strict UPPERCASE naming convention with JWT_ prefix for all property names.
8
+ */
3
9
  export class JwtManager {
10
+ /** @type {string} JWE algorithm */
11
+ algorithm;
12
+
13
+ /** @type {string} Encryption method */
14
+ encryption;
15
+
16
+ /** @type {string} Token expiration time */
17
+ expirationTime;
18
+
19
+ /** @type {number} Clock tolerance in seconds */
20
+ clockTolerance;
21
+
22
+ /** @type {string} Hash algorithm for secret derivation */
23
+ secretHashAlgorithm;
24
+
25
+ /** @type {string|null} Optional JWT issuer claim */
26
+ issuer;
27
+
28
+ /** @type {string|null} Optional JWT audience claim */
29
+ audience;
30
+
31
+ /** @type {string|null} Optional JWT subject claim */
32
+ subject;
4
33
  /**
5
34
  * 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
35
+ * Constructor options use UPPERCASE naming convention with JWT_ prefix (e.g., JWT_ALGORITHM).
36
+ *
37
+ * @typedef {Object} JwtManagerOptions JwtManager configuration options
38
+ * @property {string} [JWT_ALGORITHM='dir'] JWE algorithm (default: 'dir')
39
+ * @property {string} [JWT_ENCRYPTION='A256GCM'] Encryption method (default: 'A256GCM')
40
+ * @property {string} [JWT_EXPIRATION_TIME='10m'] Token expiration time (default: '10m')
41
+ * @property {string} [JWT_SECRET_HASH_ALGORITHM='SHA-256'] Hash algorithm (default: 'SHA-256')
42
+ * @property {string?} [JWT_ISSUER] Optional JWT issuer claim
43
+ * @property {string?} [JWT_AUDIENCE] Optional JWT audience claim
44
+ * @property {string?} [JWT_SUBJECT] Optional JWT subject claim
45
+ * @property {number} [JWT_CLOCK_TOLERANCE=30] Clock tolerance in seconds (default: 30)
46
+ * @param {JwtManagerOptions} options Configuration options
15
47
  */
16
48
  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;
49
+ this.algorithm = options.JWT_ALGORITHM || 'dir';
50
+ this.encryption = options.JWT_ENCRYPTION || 'A256GCM';
51
+ this.expirationTime = options.JWT_EXPIRATION_TIME || '10m';
52
+ this.secretHashAlgorithm = options.JWT_SECRET_HASH_ALGORITHM || 'SHA-256';
53
+ this.issuer = options.JWT_ISSUER;
54
+ this.audience = options.JWT_AUDIENCE;
55
+ this.subject = options.JWT_SUBJECT;
56
+ this.clockTolerance = options.JWT_CLOCK_TOLERANCE ?? 30;
25
57
  }
26
58
 
59
+ /**
60
+ * Encrypt method options (camelCase naming convention, uses instance defaults when not provided)
61
+ *
62
+ * @typedef {Object} JwtEncryptOptions Encryption method options
63
+ * @property {string} [algorithm='dir'] JWE algorithm (overrides instance JWT_ALGORITHM)
64
+ * @property {string} [encryption='A256GCM'] Encryption method (overrides instance JWT_ENCRYPTION)
65
+ * @property {string} [expirationTime='10m'] Token expiration time (overrides instance JWT_EXPIRATION_TIME)
66
+ * @property {string} [secretHashAlgorithm='SHA-256'] Hash algorithm for secret derivation (overrides instance JWT_SECRET_HASH_ALGORITHM)
67
+ * @property {string?} [issuer] Optional JWT issuer claim (overrides instance JWT_ISSUER)
68
+ * @property {string?} [audience] Optional JWT audience claim (overrides instance JWT_AUDIENCE)
69
+ * @property {string?} [subject] Optional JWT subject claim (overrides instance JWT_SUBJECT)
70
+ */
27
71
  /**
28
72
  * Generate JWT token for user session
73
+ *
29
74
  * @param {import('jose').JWTPayload} data User data payload
30
75
  * @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
76
+ * @param {JwtEncryptOptions} [options] Per-call configuration overrides (camelCase naming convention)
39
77
  * @returns {Promise<string>} Returns encrypted JWT token
40
78
  */
41
79
  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;
80
+ const algorithm = options.algorithm ?? this.algorithm;
81
+ const encryption = options.encryption ?? this.encryption;
82
+ const expirationTime = options.expirationTime ?? this.expirationTime;
83
+ const secretHashAlgorithm = options.secretHashAlgorithm ?? this.secretHashAlgorithm;
84
+ const issuer = options.issuer ?? this.issuer;
85
+ const audience = options.audience ?? this.audience;
86
+ const subject = options.subject ?? this.subject;
49
87
 
50
88
  const secretHash = await crypto.subtle.digest(
51
89
  secretHashAlgorithm,
@@ -69,23 +107,29 @@ export class JwtManager {
69
107
  }
70
108
 
71
109
  /**
72
- * Decrypt JWT token for user session
110
+ * Decrypt method options (camelCase naming convention, uses instance defaults when not provided)
111
+ *
112
+ * @typedef {Object} JwtDecryptOptions Decryption method options
113
+ * @property {number} [clockTolerance=30] Clock tolerance in seconds (overrides instance JWT_CLOCK_TOLERANCE)
114
+ * @property {string} [secretHashAlgorithm='SHA-256'] Hash algorithm for secret derivation (overrides instance JWT_SECRET_HASH_ALGORITHM)
115
+ * @property {string?} [issuer] Optional JWT issuer claim for validation (overrides instance JWT_ISSUER)
116
+ * @property {string?} [audience] Optional JWT audience claim for validation (overrides instance JWT_AUDIENCE)
117
+ * @property {string?} [subject] Optional JWT subject claim for validation (overrides instance JWT_SUBJECT)
118
+ **/
119
+ /**
120
+ * Decrypt JWT
121
+ *
73
122
  * @param {string} token JWT token to decrypt
74
123
  * @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
124
+ * @param {JwtDecryptOptions} [options] Per-call configuration overrides (camelCase naming convention)
125
+ * @returns {Promise<import('jose').JWTDecryptResult<import('jose').EncryptJWT>} Returns decrypted JWT token
82
126
  */
83
127
  async decrypt(token, secret, options = {}) {
84
128
  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;
129
+ const secretHashAlgorithm = options.secretHashAlgorithm ?? this.secretHashAlgorithm;
130
+ const issuer = options.issuer ?? this.issuer;
131
+ const audience = options.audience ?? this.audience;
132
+ const subject = options.subject ?? this.subject;
89
133
 
90
134
  const secretHash = await crypto.subtle.digest(
91
135
  secretHashAlgorithm,
@@ -101,4 +145,4 @@ export class JwtManager {
101
145
 
102
146
  return await jwtDecrypt(token, new Uint8Array(secretHash), decryptOptions);
103
147
  }
104
- }
148
+ }