@rapidrest/core 1.14.0 → 2.0.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.
Files changed (53) hide show
  1. package/dist/lib/AlertUtils.js +71 -56
  2. package/dist/lib/AlertUtils.js.map +1 -1
  3. package/dist/lib/ApiError.js +8 -1
  4. package/dist/lib/ApiError.js.map +1 -1
  5. package/dist/lib/CacheUtils.js +23 -0
  6. package/dist/lib/CacheUtils.js.map +1 -0
  7. package/dist/lib/ClassLoader.js +48 -44
  8. package/dist/lib/ClassLoader.js.map +1 -1
  9. package/dist/lib/FileUtils.js +133 -60
  10. package/dist/lib/FileUtils.js.map +1 -1
  11. package/dist/lib/JWTUtils.js +250 -145
  12. package/dist/lib/JWTUtils.js.map +1 -1
  13. package/dist/lib/Logger.js +39 -7
  14. package/dist/lib/Logger.js.map +1 -1
  15. package/dist/lib/MemoryStore.js +8 -6
  16. package/dist/lib/MemoryStore.js.map +1 -1
  17. package/dist/lib/MessagingUtils.js +71 -35
  18. package/dist/lib/MessagingUtils.js.map +1 -1
  19. package/dist/lib/NotificationsUtils.js +60 -8
  20. package/dist/lib/NotificationsUtils.js.map +1 -1
  21. package/dist/lib/OASUtils.js +67 -18
  22. package/dist/lib/OASUtils.js.map +1 -1
  23. package/dist/lib/ObjectFactory.js +151 -63
  24. package/dist/lib/ObjectFactory.js.map +1 -1
  25. package/dist/lib/ObjectUtils.js +48 -7
  26. package/dist/lib/ObjectUtils.js.map +1 -1
  27. package/dist/lib/StringUtils.js +55 -21
  28. package/dist/lib/StringUtils.js.map +1 -1
  29. package/dist/lib/TelemetryUtils.js +41 -5
  30. package/dist/lib/TelemetryUtils.js.map +1 -1
  31. package/dist/lib/UserUtils.js +5 -6
  32. package/dist/lib/UserUtils.js.map +1 -1
  33. package/dist/lib/ValidationUtils.js +16 -4
  34. package/dist/lib/ValidationUtils.js.map +1 -1
  35. package/dist/lib/index.js +2 -0
  36. package/dist/lib/index.js.map +1 -1
  37. package/dist/lib/threads/ThreadPool.js +198 -58
  38. package/dist/lib/threads/ThreadPool.js.map +1 -1
  39. package/dist/types/AlertUtils.d.ts +4 -0
  40. package/dist/types/CacheUtils.d.ts +14 -0
  41. package/dist/types/ClassLoader.d.ts +10 -0
  42. package/dist/types/FileUtils.d.ts +23 -7
  43. package/dist/types/JWTUtils.d.ts +88 -18
  44. package/dist/types/MessagingUtils.d.ts +7 -1
  45. package/dist/types/NotificationsUtils.d.ts +30 -0
  46. package/dist/types/ObjectFactory.d.ts +25 -1
  47. package/dist/types/ObjectUtils.d.ts +14 -0
  48. package/dist/types/StringUtils.d.ts +8 -0
  49. package/dist/types/TelemetryUtils.d.ts +19 -1
  50. package/dist/types/ValidationUtils.d.ts +1 -1
  51. package/dist/types/index.d.ts +2 -0
  52. package/dist/types/threads/ThreadPool.d.ts +34 -2
  53. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  ///////////////////////////////////////////////////////////////////////////////
2
2
  // Copyright (C) 2020-2026 Jean-Philippe Steinmetz
3
3
  ///////////////////////////////////////////////////////////////////////////////
4
- import crypto from "crypto";
4
+ import crypto, { KeyObject } from "crypto";
5
5
  import jwt from "jsonwebtoken";
6
6
  import zlib from "zlib";
7
7
  /**
@@ -19,78 +19,161 @@ export var JWTUtilsCompressionMethods;
19
19
  */
20
20
  export class JWTUtils {
21
21
  /**
22
- * Throws if `config.secret` looks like an asymmetric (RSA/EC) key but `config.options.algorithms` was not
23
- * explicitly restricted. Signing/verifying with an asymmetric key while leaving `algorithms` unset opens the
24
- * door to algorithm-confusion attacks (e.g. an attacker forging an HS256 token using the public key as the
25
- * HMAC secret). HMAC secrets (plain strings/buffers that aren't PEM-encoded) are unaffected.
22
+ * Returns `true` if `secret` is a form that is only ever usable as a genuine HMAC secret - a plain
23
+ * string/Buffer that isn't PEM-encoded, or a `KeyObject` of type `"secret"`. Anything else (a PEM
24
+ * string/Buffer, an asymmetric `KeyObject`, a `{ key, passphrase }` wrapper, a `GetPublicKeyOrSecret`
25
+ * callback, a JWK-shaped object, etc.) is *not* considered safe, since it may resolve to a non-secret
26
+ * (public) value that an attacker could use to forge an HS256/384/512 token.
27
+ */
28
+ static isKnownSafeHmacSecret(secret) {
29
+ const pemPattern = /-----BEGIN [A-Z ]*(PRIVATE|PUBLIC) KEY-----/;
30
+ if (typeof secret === "string") {
31
+ return !pemPattern.test(secret);
32
+ }
33
+ // `fs.readFileSync()` - the idiomatic way to load a key file - returns a Buffer, not a string, so both
34
+ // representations must be checked here.
35
+ if (Buffer.isBuffer(secret)) {
36
+ return !pemPattern.test(secret.toString("utf8"));
37
+ }
38
+ if (secret instanceof KeyObject) {
39
+ return secret.type === "secret";
40
+ }
41
+ return false;
42
+ }
43
+ /**
44
+ * Throws unless `config.secret` is a known-safe plain HMAC secret. Any other secret shape is treated
45
+ * conservatively as potentially asymmetric (fail closed, rather than only matching a fixed allowlist of
46
+ * asymmetric shapes seen so far) and requires `config.options.algorithms` to be explicitly set to a list
47
+ * that does not itself include an HMAC algorithm. Without this, signing/verifying with e.g. an RSA key
48
+ * while leaving `algorithms` unset - or restricting it to `["RS256", "HS256"]` - opens the door to
49
+ * algorithm-confusion attacks: an attacker holding only the public half of the key pair can forge a token
50
+ * by signing it with HS256 using that public value as the HMAC secret.
26
51
  *
27
52
  * @param config The JWT configuration to validate.
28
53
  */
29
54
  static assertSafeAlgorithm(config) {
30
55
  const secret = config.secret;
31
- const pemPattern = /-----BEGIN [A-Z ]*(PRIVATE|PUBLIC) KEY-----/;
32
- // `fs.readFileSync()` - the idiomatic way to load a key file - returns a Buffer, not a string, so both
33
- // representations must be checked or the guard below is trivially bypassed.
34
- const looksAsymmetric = (typeof secret === "string" && pemPattern.test(secret)) ||
35
- (Buffer.isBuffer(secret) && pemPattern.test(secret.toString("utf8")));
36
- if (looksAsymmetric && (!config.options?.algorithms || config.options.algorithms.length === 0)) {
56
+ if (JWTUtils.isKnownSafeHmacSecret(secret)) {
57
+ return;
58
+ }
59
+ const algorithms = config.options?.algorithms;
60
+ if (!algorithms || algorithms.length === 0) {
37
61
  throw new Error("config.secret appears to be an asymmetric key. config.options.algorithms must be explicitly set " +
38
62
  "(e.g. ['RS256']) to prevent algorithm-confusion attacks.");
39
63
  }
64
+ if (algorithms.some((alg) => JWTUtils.HMAC_ALGORITHMS.has(alg))) {
65
+ throw new Error("config.options.algorithms includes an HMAC algorithm (HS256/HS384/HS512) alongside a " +
66
+ "non-HMAC secret. This would allow an attacker holding the public half of the key to forge " +
67
+ "tokens via algorithm confusion. Remove HMAC algorithms from config.options.algorithms.");
68
+ }
40
69
  }
41
70
  /**
42
- * Generates a new JWT token for the given config and user object. The user object must be a valid RapidREST
43
- * user.
71
+ * Returns the key length, in bytes, required by `algorithm` (e.g. 32 for `aes-256-cbc`, 24 for
72
+ * `aes-192-cbc`), so the scrypt-derived key `deriveKey`/`deriveKeySync` produce always matches whatever
73
+ * cipher `passwordOptions.algorithm` actually names, instead of a length hard-coded for one specific
74
+ * algorithm. Throws a clear error for an algorithm Node's `crypto` module doesn't recognize, rather than
75
+ * letting `createCipheriv`/`createDecipheriv` fail later with an opaque "Invalid key length".
76
+ */
77
+ static resolveKeyLength(algorithm) {
78
+ const info = crypto.getCipherInfo(algorithm);
79
+ if (!info?.keyLength) {
80
+ throw new Error(`Unknown or unsupported cipher algorithm: "${algorithm}".`);
81
+ }
82
+ return info.keyLength;
83
+ }
84
+ /**
85
+ * Derives a symmetric encryption key from `password`/`salt` for use with `algorithm`.
86
+ */
87
+ static deriveKey(password, salt, algorithm) {
88
+ const keyLength = JWTUtils.resolveKeyLength(algorithm);
89
+ return new Promise((resolve, reject) => crypto.scrypt(password, salt, keyLength, (err, key) => (err ? reject(err) : resolve(key))));
90
+ }
91
+ /**
92
+ * Synchronous counterpart to `deriveKey()`. **Blocks the event loop** for the duration of the scrypt
93
+ * derivation (deliberately CPU-expensive, typically tens of milliseconds) - `createTokenSync`/
94
+ * `decodeTokenSync` should therefore be avoided on a request-handling path when password-based payload
95
+ * encryption is configured; prefer `createToken`/`decodeToken` there.
96
+ */
97
+ static deriveKeySync(password, salt, algorithm) {
98
+ return crypto.scryptSync(password, salt, JWTUtils.resolveKeyLength(algorithm));
99
+ }
100
+ /**
101
+ * Encrypts `plaintext` for `publicKey` using hybrid encryption: `plaintext` is encrypted with a fresh
102
+ * AES-256-GCM key, which is itself wrapped with RSA. This avoids `crypto.publicEncrypt`'s fixed plaintext-size
103
+ * limit (e.g. ~214 bytes for a 2048-bit key with OAEP padding), which a raw JSON user profile easily exceeds.
44
104
  *
45
- * @param config The JWT configuration to use when generating the token.
46
- * @param user The user to encode into the token's payload.
105
+ * @param publicKey The RSA public key to wrap the AES key with.
106
+ * @param plaintext The data to encrypt.
107
+ * @returns The encrypted data, encoded as `<wrapped-key>.<iv>.<authTag>.<ciphertext>`, all base64.
108
+ */
109
+ static hybridEncrypt(publicKey, plaintext) {
110
+ const aesKey = crypto.randomBytes(32);
111
+ const iv = crypto.randomBytes(12);
112
+ const cipher = crypto.createCipheriv("aes-256-gcm", aesKey, iv);
113
+ let ciphertext = cipher.update(plaintext, "utf8", "base64");
114
+ ciphertext += cipher.final("base64");
115
+ const authTag = cipher.getAuthTag();
116
+ const wrappedKey = crypto.publicEncrypt(publicKey, aesKey);
117
+ return [wrappedKey.toString("base64"), iv.toString("base64"), authTag.toString("base64"), ciphertext].join(".");
118
+ }
119
+ /**
120
+ * Returns the base64-encoded authentication tag for `cipher` if it's an AEAD cipher, otherwise `""`. Rather
121
+ * than pattern-matching the algorithm name (which only recognizes a fixed list of known AEAD naming
122
+ * conventions), this asks the cipher itself via `getAuthTag()`, which throws for any non-AEAD cipher - so
123
+ * any AEAD cipher/alias supported by the current Node/OpenSSL build is handled correctly, including ones
124
+ * not known when this was last updated.
47
125
  */
48
- static deriveKey(password, salt) {
49
- return new Promise((resolve, reject) => crypto.scrypt(password, salt, 24, (err, key) => (err ? reject(err) : resolve(key))));
126
+ static getAuthTagIfAEAD(cipher) {
127
+ try {
128
+ return cipher.getAuthTag().toString("base64");
129
+ }
130
+ catch {
131
+ return "";
132
+ }
50
133
  }
51
134
  /**
52
- * Generates a new JWT token for the given config and user object. The user object must be a valid RapidREST
53
- * user.
135
+ * Decrypts data produced by `hybridEncrypt`.
54
136
  *
55
- * @param config The JWT configuration to use when generating the token.
56
- * @param user The user to encode into the token's payload.
137
+ * @param privateKey The RSA private key to unwrap the AES key with.
138
+ * @param encoded The encrypted data, as produced by `hybridEncrypt`.
57
139
  */
58
- static deriveKeySync(password, salt) {
59
- return crypto.scryptSync(password, salt, 24);
140
+ static hybridDecrypt(privateKey, encoded) {
141
+ const parts = encoded.split(".");
142
+ // Mirrors the length check `finishPasswordDecryption`'s caller performs for the password-based format:
143
+ // without it, a malformed `profile` (e.g. from an outdated/different format) throws an untyped
144
+ // `TypeError` out of `Buffer.from(undefined, ...)` below instead of a clear, identifiable error.
145
+ if (parts.length !== 4) {
146
+ throw new Error("Encrypted profile uses an unrecognized or outdated format and cannot be decoded.");
147
+ }
148
+ const [wrappedKeyB64, ivB64, authTagB64, ciphertext] = parts;
149
+ const aesKey = crypto.privateDecrypt(privateKey, Buffer.from(wrappedKeyB64, "base64"));
150
+ const iv = Buffer.from(ivB64, "base64");
151
+ const authTag = Buffer.from(authTagB64, "base64");
152
+ const decipher = crypto.createDecipheriv("aes-256-gcm", aesKey, iv);
153
+ decipher.setAuthTag(authTag);
154
+ let decrypted = decipher.update(ciphertext, "base64", "utf8");
155
+ decrypted += decipher.final("utf8");
156
+ return decrypted;
60
157
  }
61
- static async createToken(config, user, data) {
62
- // Validate the required config options
158
+ /**
159
+ * Builds the signable payload shared by `createToken`/`createTokenSync`: validates `config`/`user`, spreads
160
+ * `data`, compresses the profile if requested, and - for public-key encryption only, which has no async
161
+ * dependency - encrypts it. Password-based encryption is left for the caller to finish via
162
+ * `finishPasswordEncryption()`, since deriving the key is the one step that differs between the sync and
163
+ * async entry points.
164
+ */
165
+ static preparePayload(config, user, data) {
63
166
  if (!config.secret) {
64
167
  throw new Error("Invalid configuration provided.");
65
168
  }
66
- // Validate the user object
67
169
  if (!user || !user.uid) {
68
170
  throw new Error("Invalid or null user object provided.");
69
171
  }
70
172
  // `data` is spread before `profile` so that a `profile` key present in caller-supplied `data` can never
71
173
  // silently override the authoritative, server-derived user profile before signing.
72
- let payload = { ...data, profile: JSON.stringify(user) };
73
- // Encrypt the profile if desired
74
- if (config.payload && config.payload.encrypt) {
75
- const payloadOptions = config.payload;
76
- if (payloadOptions.public_key) {
77
- const keyOptions = payloadOptions;
78
- const encrypted = crypto.publicEncrypt(keyOptions.public_key, Buffer.from(payload.profile));
79
- payload.profile = encrypted.toString("base64");
80
- }
81
- else {
82
- const pwOtions = payloadOptions;
83
- const iv = Buffer.from(pwOtions.iv);
84
- const salt = crypto.randomBytes(16);
85
- const key = await JWTUtils.deriveKey(pwOtions.password, salt);
86
- const cipher = crypto.createCipheriv(pwOtions.algorithm, key, iv);
87
- let encrypted = cipher.update(payload.profile, "utf8", "base64");
88
- encrypted += cipher.final("base64");
89
- payload.profile = salt.toString("base64") + ":" + encrypted;
90
- }
91
- payload.encryption = true;
92
- }
93
- // Compress the profile if desired
174
+ const payload = { ...data, profile: JSON.stringify(user) };
175
+ // Compress the profile if desired. Done *before* encryption so encryption (and, for RSA, its fixed
176
+ // plaintext-size limit) operates on the smaller compressed representation rather than the raw JSON.
94
177
  if (config.payload && config.payload.compress) {
95
178
  if (config.payload.compress === JWTUtilsCompressionMethods.ZLIB) {
96
179
  const buf = Buffer.from(payload.profile, "utf-8");
@@ -98,94 +181,124 @@ export class JWTUtils {
98
181
  payload.compression = "zlib";
99
182
  }
100
183
  }
101
- JWTUtils.assertSafeAlgorithm(config);
102
- return jwt.sign(payload, config.secret, config.options);
103
- }
104
- static createTokenSync(config, user, data) {
105
- // Validate the required config options
106
- if (!config.secret) {
107
- throw new Error("Invalid configuration provided.");
108
- }
109
- // Validate the user object
110
- if (!user || !user.uid) {
111
- throw new Error("Invalid or null user object provided.");
112
- }
113
- // `data` is spread before `profile` so that a `profile` key present in caller-supplied `data` can never
114
- // silently override the authoritative, server-derived user profile before signing.
115
- let payload = { ...data, profile: JSON.stringify(user) };
116
- // Encrypt the profile if desired
184
+ let passwordOptions;
117
185
  if (config.payload && config.payload.encrypt) {
118
186
  const payloadOptions = config.payload;
119
187
  if (payloadOptions.public_key) {
120
188
  const keyOptions = payloadOptions;
121
- const encrypted = crypto.publicEncrypt(keyOptions.public_key, Buffer.from(payload.profile));
122
- payload.profile = encrypted.toString("base64");
189
+ payload.profile = JWTUtils.hybridEncrypt(keyOptions.public_key, payload.profile);
190
+ payload.encryption = true;
123
191
  }
124
192
  else {
125
- const pwOtions = payloadOptions;
126
- const iv = Buffer.from(pwOtions.iv);
127
- const salt = crypto.randomBytes(16);
128
- const key = JWTUtils.deriveKeySync(pwOtions.password, salt);
129
- const cipher = crypto.createCipheriv(pwOtions.algorithm, key, iv);
130
- let encrypted = cipher.update(payload.profile, "utf8", "base64");
131
- encrypted += cipher.final("base64");
132
- payload.profile = salt.toString("base64") + ":" + encrypted;
193
+ passwordOptions = payloadOptions;
133
194
  }
134
- payload.encryption = true;
135
195
  }
136
- // Compress the profile if desired
137
- if (config.payload && config.payload.compress) {
138
- if (config.payload.compress === JWTUtilsCompressionMethods.ZLIB) {
139
- const buf = Buffer.from(payload.profile, "utf-8");
140
- payload.profile = zlib.gzipSync(buf).toString("base64");
141
- payload.compression = "zlib";
142
- }
196
+ return { payload, passwordOptions };
197
+ }
198
+ /** Finishes password-based payload encryption once `key` has been derived (sync or async). */
199
+ static finishPasswordEncryption(payload, passwordOptions, key, salt) {
200
+ const iv = Buffer.from(passwordOptions.iv);
201
+ const cipher = crypto.createCipheriv(passwordOptions.algorithm, key, iv);
202
+ let encrypted = cipher.update(payload.profile, "utf8", "base64");
203
+ encrypted += cipher.final("base64");
204
+ // AEAD ciphers (e.g. aes-256-gcm) require the auth tag to be captured here and verified on decrypt via
205
+ // setAuthTag(), otherwise decoding throws "Unsupported state or unable to authenticate data" on every
206
+ // token. `final()` must be called before `getAuthTag()` - the tag isn't available until encryption
207
+ // has finished.
208
+ const authTag = JWTUtils.getAuthTagIfAEAD(cipher);
209
+ payload.profile = salt.toString("base64") + ":" + authTag + ":" + encrypted;
210
+ payload.encryption = true;
211
+ }
212
+ static async createToken(config, user, data) {
213
+ const { payload, passwordOptions } = JWTUtils.preparePayload(config, user, data);
214
+ if (passwordOptions) {
215
+ const salt = crypto.randomBytes(16);
216
+ const key = await JWTUtils.deriveKey(passwordOptions.password, salt, passwordOptions.algorithm);
217
+ JWTUtils.finishPasswordEncryption(payload, passwordOptions, key, salt);
143
218
  }
144
219
  JWTUtils.assertSafeAlgorithm(config);
145
220
  return jwt.sign(payload, config.secret, config.options);
146
221
  }
147
222
  /**
148
- * Decodes the given JWT authentication token using the provided configuration. If the token is not valid an
149
- * error is thrown with the reason. Returns the encoded user object payload upon success.
150
- *
151
- * @param config The JWT configuration to use when validating the token.
152
- * @param token The JWT token to validate.
153
- * @returns The data encoded in the token's payload.
223
+ * Synchronous counterpart to `createToken()`. **Blocks the event loop** while deriving the encryption key
224
+ * when password-based payload encryption is configured (see `deriveKeySync`) - prefer `createToken()` on a
225
+ * request-handling path in that case.
154
226
  */
155
- static async decodeToken(config, token) {
227
+ static createTokenSync(config, user, data) {
228
+ const { payload, passwordOptions } = JWTUtils.preparePayload(config, user, data);
229
+ if (passwordOptions) {
230
+ const salt = crypto.randomBytes(16);
231
+ const key = JWTUtils.deriveKeySync(passwordOptions.password, salt, passwordOptions.algorithm);
232
+ JWTUtils.finishPasswordEncryption(payload, passwordOptions, key, salt);
233
+ }
156
234
  JWTUtils.assertSafeAlgorithm(config);
157
- // Decode the token
158
- let payload = jwt.verify(token, config.secret, config.options);
159
- // Validate the payload
235
+ return jwt.sign(payload, config.secret, config.options);
236
+ }
237
+ /**
238
+ * Verifies `token` and prepares its payload for `decodeToken`/`decodeTokenSync`: validates the signature/
239
+ * shape, and - for private-key decryption only, which has no async dependency - decrypts it in place.
240
+ * Password-based decryption is left for the caller to finish via `finishPasswordDecryption()`, since
241
+ * deriving the key is the one step that differs between the sync and async entry points.
242
+ */
243
+ static preDecode(config, token) {
244
+ JWTUtils.assertSafeAlgorithm(config);
245
+ const payload = jwt.verify(token, config.secret, config.options);
160
246
  if (!payload || !payload.profile) {
161
247
  throw new Error("Token is invalid or missing data.");
162
248
  }
163
- // Decompress the payload if desired
164
- if (payload.compression === JWTUtilsCompressionMethods.ZLIB) {
165
- const buf = Buffer.from(payload.profile, "base64");
166
- payload.profile = zlib.gunzipSync(buf).toString("utf-8");
167
- }
168
- // Decrypt the payload if desired
249
+ let passwordOptions;
250
+ let salt;
251
+ let authTagB64;
252
+ let encryptedProfile;
253
+ // Decrypt the payload if desired. Must happen before decompression since compression is applied *before*
254
+ // encryption when the token is created, so decryption must be undone first.
169
255
  if (payload.encryption && config.payload && config.payload.encrypt) {
170
256
  const payloadOptions = config.payload;
171
257
  if (payloadOptions.private_key) {
172
258
  const keyOptions = payloadOptions;
173
- const decrypted = crypto.privateDecrypt(keyOptions.private_key, Buffer.from(payload.profile, "base64"));
174
- payload.profile = decrypted.toString("utf8");
259
+ payload.profile = JWTUtils.hybridDecrypt(keyOptions.private_key, payload.profile);
175
260
  }
176
261
  else {
177
- const pwOtions = payloadOptions;
178
- const iv = Buffer.from(pwOtions.iv);
179
- const [saltB64, profile] = payload.profile.split(":");
180
- const salt = Buffer.from(saltB64, "base64");
181
- const key = await JWTUtils.deriveKey(pwOtions.password, salt);
182
- const decipher = crypto.createDecipheriv(pwOtions.algorithm, key, iv);
183
- let decrypted = decipher.update(profile, "base64", "utf8");
184
- decrypted += decipher.final("utf8");
185
- payload.profile = decrypted;
262
+ passwordOptions = payloadOptions;
263
+ const parts = payload.profile.split(":");
264
+ // The current format is `<salt>:<authTag>:<ciphertext>` (3 parts). A 2-part `<salt>:<ciphertext>`
265
+ // payload is the pre-AEAD-auth-tag format from before this method required an auth tag; without
266
+ // this check, `encryptedProfile` would silently come back `undefined` and decryption would be
267
+ // skipped, surfacing as an opaque JSON.parse SyntaxError in finalizePayload() instead of a clear
268
+ // error identifying the actual problem.
269
+ if (parts.length !== 3) {
270
+ throw new Error("Token payload uses an unrecognized or outdated encrypted format and cannot be decoded.");
271
+ }
272
+ const [saltB64, tagB64, profile] = parts;
273
+ salt = Buffer.from(saltB64, "base64");
274
+ authTagB64 = tagB64;
275
+ encryptedProfile = profile;
186
276
  }
187
277
  }
188
- // Make sure the profile is an parsed
278
+ return { payload, passwordOptions, salt, authTagB64, encryptedProfile };
279
+ }
280
+ /** Finishes password-based payload decryption once `key` has been derived (sync or async). */
281
+ static finishPasswordDecryption(payload, passwordOptions, key, encryptedProfile, authTagB64) {
282
+ const iv = Buffer.from(passwordOptions.iv);
283
+ const decipher = crypto.createDecipheriv(passwordOptions.algorithm, key, iv);
284
+ if (authTagB64) {
285
+ decipher.setAuthTag(Buffer.from(authTagB64, "base64"));
286
+ }
287
+ let decrypted = decipher.update(encryptedProfile, "base64", "utf8");
288
+ decrypted += decipher.final("utf8");
289
+ payload.profile = decrypted;
290
+ }
291
+ /** Decompresses (if applicable) and parses the final payload profile shared by both decode entry points. */
292
+ static finalizePayload(payload) {
293
+ if (payload.compression === JWTUtilsCompressionMethods.ZLIB) {
294
+ const buf = Buffer.from(payload.profile, "base64");
295
+ // Cap decompressed size against a decompression bomb (a small compressed payload expanding to consume
296
+ // excessive memory). Requires a validly-signed token, but costs nothing to bound given every other
297
+ // cache/store in this codebase is similarly size-limited.
298
+ payload.profile = zlib
299
+ .gunzipSync(buf, { maxOutputLength: JWTUtils.MAX_DECOMPRESSED_PROFILE_BYTES })
300
+ .toString("utf-8");
301
+ }
189
302
  payload.profile = JSON.parse(payload.profile);
190
303
  return payload;
191
304
  }
@@ -197,43 +310,35 @@ export class JWTUtils {
197
310
  * @param token The JWT token to validate.
198
311
  * @returns The data encoded in the token's payload.
199
312
  */
200
- static decodeTokenSync(config, token) {
201
- JWTUtils.assertSafeAlgorithm(config);
202
- // Decode the token
203
- let payload = jwt.verify(token, config.secret, config.options);
204
- // Validate the payload
205
- if (!payload || !payload.profile) {
206
- throw new Error("Token is invalid or missing data.");
207
- }
208
- // Decompress the payload if desired
209
- if (payload.compression === JWTUtilsCompressionMethods.ZLIB) {
210
- const buf = Buffer.from(payload.profile, "base64");
211
- payload.profile = zlib.gunzipSync(buf).toString("utf-8");
313
+ static async decodeToken(config, token) {
314
+ const { payload, passwordOptions, salt, authTagB64, encryptedProfile } = JWTUtils.preDecode(config, token);
315
+ if (passwordOptions && salt && encryptedProfile !== undefined) {
316
+ const key = await JWTUtils.deriveKey(passwordOptions.password, salt, passwordOptions.algorithm);
317
+ JWTUtils.finishPasswordDecryption(payload, passwordOptions, key, encryptedProfile, authTagB64);
212
318
  }
213
- // Decrypt the payload if desired
214
- if (payload.encryption && config.payload && config.payload.encrypt) {
215
- const payloadOptions = config.payload;
216
- if (payloadOptions.private_key) {
217
- const keyOptions = payloadOptions;
218
- const decrypted = crypto.privateDecrypt(keyOptions.private_key, Buffer.from(payload.profile, "base64"));
219
- payload.profile = decrypted.toString("utf8");
220
- }
221
- else {
222
- const pwOtions = payloadOptions;
223
- const iv = Buffer.from(pwOtions.iv);
224
- const [saltB64, profile] = payload.profile.split(":");
225
- const salt = Buffer.from(saltB64, "base64");
226
- const key = JWTUtils.deriveKeySync(pwOtions.password, salt);
227
- const decipher = crypto.createDecipheriv(pwOtions.algorithm, key, iv);
228
- let decrypted = decipher.update(profile, "base64", "utf8");
229
- decrypted += decipher.final("utf8");
230
- payload.profile = decrypted;
231
- }
319
+ return JWTUtils.finalizePayload(payload);
320
+ }
321
+ /**
322
+ * Synchronous counterpart to `decodeToken()`. **Blocks the event loop** while deriving the decryption key
323
+ * when password-based payload encryption is configured (see `deriveKeySync`) - prefer `decodeToken()` on a
324
+ * request-handling path in that case.
325
+ *
326
+ * @param config The JWT configuration to use when validating the token.
327
+ * @param token The JWT token to validate.
328
+ * @returns The data encoded in the token's payload.
329
+ */
330
+ static decodeTokenSync(config, token) {
331
+ const { payload, passwordOptions, salt, authTagB64, encryptedProfile } = JWTUtils.preDecode(config, token);
332
+ if (passwordOptions && salt && encryptedProfile !== undefined) {
333
+ const key = JWTUtils.deriveKeySync(passwordOptions.password, salt, passwordOptions.algorithm);
334
+ JWTUtils.finishPasswordDecryption(payload, passwordOptions, key, encryptedProfile, authTagB64);
232
335
  }
233
- // Make sure the profile is an parsed
234
- payload.profile = JSON.parse(payload.profile);
235
- return payload;
336
+ return JWTUtils.finalizePayload(payload);
236
337
  }
237
338
  }
238
- JWTUtils._parsedKeyCache = new Map();
339
+ /** HMAC algorithm names that must never be permitted alongside a non-HMAC secret (see `assertSafeAlgorithm`). */
340
+ JWTUtils.HMAC_ALGORITHMS = new Set(["HS256", "HS384", "HS512"]);
341
+ /** Maximum size, in bytes, a compressed profile is allowed to decompress to (see `finalizePayload`). Guards
342
+ * against a decompression bomb - a small compressed payload expanding to consume excessive memory. */
343
+ JWTUtils.MAX_DECOMPRESSED_PROFILE_BYTES = 10 * 1024 * 1024;
239
344
  //# sourceMappingURL=JWTUtils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"JWTUtils.js","sourceRoot":"","sources":["../../src/JWTUtils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"JWTUtils.js","sourceRoot":"","sources":["../../src/JWTUtils.ts"],"names":[],"mappings":"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"}
@@ -3,13 +3,10 @@
3
3
  ///////////////////////////////////////////////////////////////////////////////
4
4
  import { fileURLToPath } from "url";
5
5
  import winston from "winston";
6
+ import { CacheUtils } from "./CacheUtils.js";
6
7
  const { format, transports } = winston;
7
8
  const { combine, timestamp, printf } = format;
8
9
  export const FILENAME = fileURLToPath(import.meta.url).replace(/\\/g, "/");
9
- // The default stack trace limit (10) is too shallow once a few frames of winston/logform internals are on the
10
- // stack, causing the real caller frame that `source()` below looks for to be truncated. Widen it so the caller is
11
- // always captured.
12
- Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 30);
13
10
  export const logFormat = printf((info) => {
14
11
  const prefix = info.source ? `[${info.source}] ` : "";
15
12
  return `${prefix}${info.timestamp} ${info.level}: ${info.message}`;
@@ -24,7 +21,14 @@ export const logFormat = printf((info) => {
24
21
  * call stack in either case.
25
22
  */
26
23
  export const source = format((info) => {
24
+ // The default stack trace limit (10) is too shallow once a few frames of winston/logform internals are on
25
+ // the stack, causing the real caller frame below to be truncated. The limit is widened only for the duration
26
+ // of this capture (rather than mutated globally for the whole process) so every other `Error` thrown
27
+ // anywhere in the application doesn't pay the cost of a permanently-widened trace.
28
+ const originalStackTraceLimit = Error.stackTraceLimit;
29
+ Error.stackTraceLimit = Math.max(originalStackTraceLimit, 30);
27
30
  const stack = new Error().stack?.split("\n") ?? [];
31
+ Error.stackTraceLimit = originalStackTraceLimit;
28
32
  for (const line of stack) {
29
33
  if (line.includes(FILENAME) || line.includes("node_modules") || line.includes("node:internal")) {
30
34
  continue;
@@ -44,6 +48,11 @@ export const source = format((info) => {
44
48
  }
45
49
  return info;
46
50
  });
51
+ /** Maximum number of distinct `${level}:${file}` logger instances to keep cached. Each `file`-bound entry holds
52
+ * open file descriptors via winston's `File` transport, so an unbounded cache would leak descriptors indefinitely
53
+ * for any caller that varies `file` per call (e.g. a per-request or per-session name) instead of reusing a fixed
54
+ * small set of names. */
55
+ const MAX_CACHED_LOGGERS = 100;
47
56
  const _loggerCache = new Map();
48
57
  /**
49
58
  * Creates (or retrieves a cached) logger with the specified level and file name to output logs to.
@@ -54,18 +63,41 @@ const _loggerCache = new Map();
54
63
  export const Logger = function (level = "debug", file = undefined) {
55
64
  const cacheKey = `${level}:${file ?? ""}`;
56
65
  if (_loggerCache.has(cacheKey)) {
57
- return _loggerCache.get(cacheKey);
66
+ const cached = _loggerCache.get(cacheKey);
67
+ // Move to the end of the Map's iteration order so a frequently-reused logger isn't evicted (and its
68
+ // file transport closed, possibly mid-write) ahead of one that's actually gone idle - Map.set() on an
69
+ // already-present key doesn't reorder it by itself.
70
+ _loggerCache.delete(cacheKey);
71
+ _loggerCache.set(cacheKey, cached);
72
+ return cached;
58
73
  }
59
- const transport = [new transports.Console()];
74
+ // `colorize()` wraps `info.level` in ANSI escape codes unconditionally - it doesn't detect whether the
75
+ // destination is a TTY. It must stay Console-only rather than living in the logger-level `format` below:
76
+ // winston applies the logger-level format to every transport that doesn't supply its own override -
77
+ // including the File transports, and any transport a caller adds later via `logger.add()` - so baking
78
+ // colorize() into it would corrupt on-disk logs and any other attached transport with escape sequences that
79
+ // break grep/log shippers/aggregators expecting a plain-text level field. The logger-level format is left as
80
+ // the uncolored base precisely so those other transports still inherit sensible plain-text formatting.
81
+ const base = combine(format.splat(), format.simple(), timestamp(), source(), logFormat);
82
+ const transport = [new transports.Console({ format: combine(format.colorize(), base) })];
60
83
  if (file) {
61
84
  transport.push(new winston.transports.File({ filename: file + "error.log", level: "error" }));
62
85
  transport.push(new winston.transports.File({ filename: file + ".log" }));
63
86
  }
64
87
  const logger = winston.createLogger({
65
88
  level,
66
- format: combine(format.splat(), format.simple(), format.colorize(), timestamp(), source(), logFormat),
89
+ format: base,
67
90
  transports: transport,
68
91
  });
92
+ if (_loggerCache.size >= MAX_CACHED_LOGGERS) {
93
+ // Non-null: the size check above guarantees the cache is non-empty, so `.next()` always yields a real key.
94
+ const oldestKey = _loggerCache.keys().next().value;
95
+ const oldest = _loggerCache.get(oldestKey);
96
+ CacheUtils.evictOldest(_loggerCache);
97
+ // Close the evicted logger's transports so its file descriptors are actually released, not just
98
+ // dropped from the cache while still held open by winston.
99
+ oldest?.close?.();
100
+ }
69
101
  _loggerCache.set(cacheKey, logger);
70
102
  return logger;
71
103
  };