@forklaunch/core 1.5.19 → 1.6.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.
@@ -733,17 +733,20 @@ async function discriminateAuthMethod(auth, openTelemetryCollector) {
733
733
  jwks = [jwt.jwksPublicKey];
734
734
  }
735
735
  verificationFunction = async (token) => {
736
+ let lastError;
736
737
  for (const key of jwks) {
737
738
  try {
738
739
  const { payload } = await jwtVerify(token, key);
739
740
  return payload;
740
- } catch {
741
+ } catch (error) {
742
+ lastError = error;
741
743
  cachedJwks.value = null;
742
744
  cachedJwks.lastUpdated = null;
743
745
  cachedJwks.ttl = DEFAULT_TTL;
744
746
  continue;
745
747
  }
746
748
  }
749
+ if (lastError !== void 0) throw lastError;
747
750
  };
748
751
  }
749
752
  authMethod = {
@@ -812,6 +815,58 @@ function hasSubscriptionChecks(maybeSubscriptionAuth) {
812
815
  return typeof maybeSubscriptionAuth === "object" && maybeSubscriptionAuth !== null && "requireActiveSubscription" in maybeSubscriptionAuth && maybeSubscriptionAuth.requireActiveSubscription === true;
813
816
  }
814
817
 
818
+ // src/http/middleware/request/authLogContext.ts
819
+ import { createHash } from "crypto";
820
+ import { decodeJwt, decodeProtectedHeader, errors as joseErrors } from "jose";
821
+ var FINGERPRINT_LENGTH = 8;
822
+ var MAX_CLAIM_LENGTH = 128;
823
+ function bounded(value) {
824
+ return typeof value === "string" && value.length > 0 ? value.slice(0, MAX_CLAIM_LENGTH) : void 0;
825
+ }
826
+ function authLogContext(headerValue) {
827
+ if (typeof headerValue !== "string" || headerValue.length === 0) {
828
+ return { hasToken: false };
829
+ }
830
+ const context2 = {
831
+ hasToken: true,
832
+ tokenFingerprint: createHash("sha256").update(headerValue).digest("hex").slice(0, FINGERPRINT_LENGTH)
833
+ };
834
+ const parts = headerValue.trim().split(/\s+/);
835
+ const candidate = parts[parts.length - 1];
836
+ if (candidate.split(".").length !== 3) {
837
+ return context2;
838
+ }
839
+ try {
840
+ const payload = decodeJwt(candidate);
841
+ const header = decodeProtectedHeader(candidate);
842
+ context2.claimed = {
843
+ sub: bounded(payload.sub),
844
+ organizationId: bounded(payload.organizationId),
845
+ iss: bounded(payload.iss),
846
+ exp: typeof payload.exp === "number" ? payload.exp : void 0,
847
+ kid: bounded(header.kid)
848
+ };
849
+ } catch {
850
+ }
851
+ return context2;
852
+ }
853
+ function jwtFailureReason(error) {
854
+ if (error instanceof joseErrors.JWTExpired) return "jwt_expired";
855
+ if (error instanceof joseErrors.JWTClaimValidationFailed) {
856
+ return `jwt_claim_${error.claim}`;
857
+ }
858
+ if (error instanceof joseErrors.JWSSignatureVerificationFailed) {
859
+ return "jwt_bad_signature";
860
+ }
861
+ if (error instanceof joseErrors.JWKSNoMatchingKey) return "jwks_no_key";
862
+ if (error instanceof joseErrors.JWKSTimeout) return "jwks_unavailable";
863
+ if (error instanceof joseErrors.JWTInvalid || error instanceof joseErrors.JWSInvalid) {
864
+ return "jwt_malformed";
865
+ }
866
+ if (error instanceof joseErrors.JOSEError) return error.code.toLowerCase();
867
+ return "unknown";
868
+ }
869
+
815
870
  // src/http/middleware/request/auth.middleware.ts
816
871
  var invalidAuthorizationTokenFormat = [
817
872
  401,
@@ -927,13 +982,14 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
927
982
  }
928
983
  sessionPayload = decodedJwt;
929
984
  } catch (error) {
930
- req?.openTelemetryCollector?.error(
985
+ req?.openTelemetryCollector?.warn(
931
986
  "JWT Verification Failed",
932
987
  meta({
933
- error,
988
+ reason: jwtFailureReason(error),
989
+ error: error instanceof Error ? error.message : String(error),
934
990
  method: req.method,
935
991
  path: req.path,
936
- token
992
+ ...authLogContext(token)
937
993
  })
938
994
  );
939
995
  return invalidAuthorizationToken;
@@ -1084,13 +1140,13 @@ async function parseRequestAuth(req, res, next) {
1084
1140
  const token = req.headers[auth?.headerName ?? "Authorization"] || req.headers[auth?.headerName ?? "authorization"];
1085
1141
  const [error, message] = await checkAuthorizationToken(req, auth, token, req._globalOptions?.()?.auth, access) ?? [];
1086
1142
  if (error != null) {
1087
- req.openTelemetryCollector?.error(
1143
+ req.openTelemetryCollector?.warn(
1088
1144
  message || "Authorization Failed",
1089
1145
  meta({
1090
1146
  statusCode: error,
1091
1147
  method: req.method,
1092
1148
  path: req.path,
1093
- token
1149
+ ...authLogContext(token)
1094
1150
  })
1095
1151
  );
1096
1152
  res.type("text/plain");
@@ -4319,7 +4375,7 @@ function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, a
4319
4375
  }
4320
4376
 
4321
4377
  // src/http/telemetry/auditLogger.ts
4322
- import { createHash } from "crypto";
4378
+ import { createHash as createHash2 } from "crypto";
4323
4379
  var AuditLogger = class {
4324
4380
  _otel;
4325
4381
  constructor(otel) {
@@ -4352,7 +4408,7 @@ var AuditLogger = class {
4352
4408
  if (body === void 0 || body === null || body === "") {
4353
4409
  return "";
4354
4410
  }
4355
- return createHash("sha256").update(body).digest("hex");
4411
+ return createHash2("sha256").update(body).digest("hex");
4356
4412
  }
4357
4413
  };
4358
4414