@forklaunch/core 0.14.9 → 0.14.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/lib/http/index.js CHANGED
@@ -158,6 +158,7 @@ function isTypedHandler(maybeTypedHandler) {
158
158
 
159
159
  // src/http/middleware/request/auth.middleware.ts
160
160
  var import_common3 = require("@forklaunch/common");
161
+ var import_validator = require("@forklaunch/validator");
161
162
 
162
163
  // src/http/discriminateAuthMethod.ts
163
164
  var import_jose = require("jose");
@@ -176,10 +177,12 @@ function createHmacToken({
176
177
  const hmac = (0, import_crypto.createHmac)("sha256", secretKey);
177
178
  const bodyString = body ? `${(0, import_common2.safeStringify)(body)}
178
179
  ` : void 0;
179
- hmac.update(`${method}
180
+ hmac.update(
181
+ `${method}
180
182
  ${path}
181
- ${bodyString}${timestamp}
182
- ${nonce}`);
183
+ ${bodyString}${timestamp.toISOString()}
184
+ ${nonce}`
185
+ );
183
186
  return hmac.digest("base64");
184
187
  }
185
188
 
@@ -199,6 +202,12 @@ function isJwtAuthMethod(maybeJwtAuthMethod) {
199
202
  }
200
203
 
201
204
  // src/http/discriminateAuthMethod.ts
205
+ var DEFAULT_TTL = 60 * 1e3 * 5;
206
+ var memoizedJwks = {
207
+ value: null,
208
+ lastUpdated: null,
209
+ ttl: DEFAULT_TTL
210
+ };
202
211
  async function discriminateAuthMethod(auth) {
203
212
  let authMethod;
204
213
  if (isBasicAuthMethod(auth)) {
@@ -223,8 +232,17 @@ async function discriminateAuthMethod(auth) {
223
232
  } else {
224
233
  let jwks;
225
234
  if ("jwksPublicKeyUrl" in jwt) {
226
- const jwksResponse = await fetch(jwt.jwksPublicKeyUrl);
227
- jwks = (await jwksResponse.json()).keys;
235
+ if (memoizedJwks.value && memoizedJwks.lastUpdated && Date.now() - memoizedJwks.lastUpdated.getTime() < memoizedJwks.ttl) {
236
+ jwks = memoizedJwks.value;
237
+ } else {
238
+ const jwksResponse = await fetch(jwt.jwksPublicKeyUrl);
239
+ jwks = (await jwksResponse.json()).keys;
240
+ memoizedJwks.value = jwks;
241
+ memoizedJwks.lastUpdated = /* @__PURE__ */ new Date();
242
+ memoizedJwks.ttl = parseInt(
243
+ jwksResponse.headers.get("cache-control")?.split("=")[1] ?? `${DEFAULT_TTL / 1e3}`
244
+ ) * 1e3;
245
+ }
228
246
  } else if ("jwksPublicKey" in jwt) {
229
247
  jwks = [jwt.jwksPublicKey];
230
248
  }
@@ -234,6 +252,9 @@ async function discriminateAuthMethod(auth) {
234
252
  const { payload } = await (0, import_jose.jwtVerify)(token, key);
235
253
  return payload;
236
254
  } catch {
255
+ memoizedJwks.value = null;
256
+ memoizedJwks.lastUpdated = null;
257
+ memoizedJwks.ttl = DEFAULT_TTL;
237
258
  continue;
238
259
  }
239
260
  }
@@ -337,7 +358,7 @@ function parseHmacTokenPart(part, expectedKey) {
337
358
  if (key !== expectedKey || rest.length === 0) return void 0;
338
359
  return rest.join("=");
339
360
  }
340
- async function checkAuthorizationToken(authorizationMethod, globalOptions, authorizationToken, req) {
361
+ async function checkAuthorizationToken(req, authorizationMethod, authorizationToken, globalOptions) {
341
362
  if (authorizationMethod == null) {
342
363
  return void 0;
343
364
  }
@@ -352,7 +373,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
352
373
  if (!tokenParts.length || !tokenPrefix) {
353
374
  return invalidAuthorizationTokenFormat;
354
375
  }
355
- let resourceId;
376
+ let sessionPayload;
356
377
  const { type, auth } = await discriminateAuthMethod(
357
378
  collapsedAuthorizationMethod
358
379
  );
@@ -376,7 +397,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
376
397
  method: req?.method ?? "",
377
398
  path: req?.path ?? "",
378
399
  body: req?.body,
379
- timestamp: parsedTimestamp,
400
+ timestamp: new Date(parsedTimestamp),
380
401
  nonce: parsedNonce,
381
402
  signature: parsedSignature,
382
403
  secretKey: collapsedAuthorizationMethod.hmac.secretKeys[parsedKeyId]
@@ -384,7 +405,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
384
405
  if (!verificationResult) {
385
406
  return invalidAuthorizationSignature;
386
407
  }
387
- resourceId = null;
408
+ sessionPayload = null;
388
409
  break;
389
410
  }
390
411
  case "jwt": {
@@ -397,7 +418,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
397
418
  if (!decodedJwt) {
398
419
  return invalidAuthorizationToken;
399
420
  }
400
- resourceId = decodedJwt;
421
+ sessionPayload = decodedJwt;
401
422
  } catch (error) {
402
423
  req?.openTelemetryCollector.error(error);
403
424
  return invalidAuthorizationToken;
@@ -410,7 +431,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
410
431
  return invalidAuthorizationTokenFormat;
411
432
  }
412
433
  if (auth.decodeResource) {
413
- resourceId = await auth.decodeResource(token);
434
+ sessionPayload = await auth.decodeResource(token);
414
435
  } else {
415
436
  const [username, password] = Buffer.from(token, "base64").toString("utf-8").split(":");
416
437
  if (!username || !password) {
@@ -419,7 +440,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
419
440
  if (!auth.login(username, password)) {
420
441
  return invalidAuthorizationLogin;
421
442
  }
422
- resourceId = {
443
+ sessionPayload = {
423
444
  sub: username
424
445
  };
425
446
  }
@@ -429,16 +450,29 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
429
450
  (0, import_common3.isNever)(type);
430
451
  return [401, "Invalid Authorization method."];
431
452
  }
432
- if (isHmacMethod(collapsedAuthorizationMethod) && resourceId == null) {
453
+ if (isHmacMethod(collapsedAuthorizationMethod) && sessionPayload == null) {
433
454
  return;
434
455
  }
435
- if (resourceId == null) {
456
+ if (sessionPayload == null) {
436
457
  return invalidAuthorizationToken;
437
458
  }
459
+ req.session = sessionPayload;
460
+ if (collapsedAuthorizationMethod.sessionSchema) {
461
+ const parsedSession = req.schemaValidator.parse(
462
+ collapsedAuthorizationMethod.sessionSchema,
463
+ sessionPayload
464
+ );
465
+ if (!parsedSession.ok) {
466
+ return [
467
+ 400,
468
+ `Invalid session: ${(0, import_validator.prettyPrintParseErrors)(parsedSession.errors, "Session")}`
469
+ ];
470
+ }
471
+ }
438
472
  if (hasScopeChecks(collapsedAuthorizationMethod)) {
439
473
  if (collapsedAuthorizationMethod.surfaceScopes) {
440
474
  const resourceScopes = await collapsedAuthorizationMethod.surfaceScopes(
441
- resourceId,
475
+ sessionPayload,
442
476
  req
443
477
  );
444
478
  if (collapsedAuthorizationMethod.scopeHeirarchy) {
@@ -457,7 +491,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
457
491
  return [500, "No permission surfacing function provided."];
458
492
  }
459
493
  const resourcePermissions = await collapsedAuthorizationMethod.surfacePermissions(
460
- resourceId,
494
+ sessionPayload,
461
495
  req
462
496
  );
463
497
  if ("allowedPermissions" in collapsedAuthorizationMethod && collapsedAuthorizationMethod.allowedPermissions) {
@@ -479,7 +513,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
479
513
  return [500, "No role surfacing function provided."];
480
514
  }
481
515
  const resourceRoles = await collapsedAuthorizationMethod.surfaceRoles(
482
- resourceId,
516
+ sessionPayload,
483
517
  req
484
518
  );
485
519
  if ("allowedRoles" in collapsedAuthorizationMethod && collapsedAuthorizationMethod.allowedRoles) {
@@ -499,10 +533,10 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
499
533
  async function parseRequestAuth(req, res, next) {
500
534
  const auth = req.contractDetails.auth;
501
535
  const [error, message] = await checkAuthorizationToken(
536
+ req,
502
537
  auth,
503
- req._globalOptions?.()?.auth,
504
538
  req.headers[auth?.headerName ?? "Authorization"] || req.headers[auth?.headerName ?? "authorization"],
505
- req
539
+ req._globalOptions?.()?.auth
506
540
  ) ?? [];
507
541
  if (error != null) {
508
542
  res.type("text/plain");
@@ -871,7 +905,7 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas, op
871
905
 
872
906
  // src/http/middleware/request/parse.middleware.ts
873
907
  var import_common8 = require("@forklaunch/common");
874
- var import_validator = require("@forklaunch/validator");
908
+ var import_validator2 = require("@forklaunch/validator");
875
909
 
876
910
  // src/http/guards/hasSend.ts
877
911
  function hasSend(res) {
@@ -909,7 +943,7 @@ function parse(req, res, next) {
909
943
  req.version = version;
910
944
  res.version = req.version;
911
945
  } else {
912
- runningParseErrors += (0, import_validator.prettyPrintParseErrors)(
946
+ runningParseErrors += (0, import_validator2.prettyPrintParseErrors)(
913
947
  parsingResult.errors,
914
948
  `Version ${version} request`
915
949
  );
@@ -967,7 +1001,7 @@ function parse(req, res, next) {
967
1001
  res.status(400);
968
1002
  if (hasSend(res)) {
969
1003
  res.send(
970
- `${collectedParseErrors ?? (0, import_validator.prettyPrintParseErrors)(parsedRequest.errors, "Request")}
1004
+ `${collectedParseErrors ?? (0, import_validator2.prettyPrintParseErrors)(parsedRequest.errors, "Request")}
971
1005
 
972
1006
  Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
973
1007
  );
@@ -977,7 +1011,7 @@ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
977
1011
  return;
978
1012
  case "warning":
979
1013
  req.openTelemetryCollector.warn(
980
- collectedParseErrors ?? (0, import_validator.prettyPrintParseErrors)(parsedRequest.errors, "Request")
1014
+ collectedParseErrors ?? (0, import_validator2.prettyPrintParseErrors)(parsedRequest.errors, "Request")
981
1015
  );
982
1016
  break;
983
1017
  case "none":
@@ -3261,7 +3295,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3261
3295
  }
3262
3296
 
3263
3297
  // src/http/middleware/response/parse.middleware.ts
3264
- var import_validator2 = require("@forklaunch/validator");
3298
+ var import_validator3 = require("@forklaunch/validator");
3265
3299
 
3266
3300
  // src/http/guards/isResponseCompiledSchema.ts
3267
3301
  function isResponseCompiledSchema(schema) {
@@ -3322,13 +3356,13 @@ function parse2(req, res, next) {
3322
3356
  );
3323
3357
  const parseErrors = [];
3324
3358
  if (!parsedHeaders.ok) {
3325
- const headerErrors = (0, import_validator2.prettyPrintParseErrors)(parsedHeaders.errors, "Header");
3359
+ const headerErrors = (0, import_validator3.prettyPrintParseErrors)(parsedHeaders.errors, "Header");
3326
3360
  if (headerErrors) {
3327
3361
  parseErrors.push(headerErrors);
3328
3362
  }
3329
3363
  }
3330
3364
  if (!parsedResponse.ok) {
3331
- const responseErrors = (0, import_validator2.prettyPrintParseErrors)(
3365
+ const responseErrors = (0, import_validator3.prettyPrintParseErrors)(
3332
3366
  parsedResponse.errors,
3333
3367
  "Response"
3334
3368
  );