@conduitplatform/module-tools 0.17.0-alpha.8 → 0.17.0-alpha.9

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/dist/index.cjs CHANGED
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ AUTHENTICATION_CONFIG_UPDATE_CHANNEL: () => AUTHENTICATION_CONFIG_UPDATE_CHANNEL,
34
+ AccessTokenVerifier: () => AccessTokenVerifier,
33
35
  ConduitActiveSchema: () => ConduitActiveSchema,
34
36
  ConduitBoolean: () => ConduitBoolean,
35
37
  ConduitDate: () => ConduitDate,
@@ -40,6 +42,7 @@ __export(index_exports, {
40
42
  ConduitServiceModule: () => ConduitServiceModule,
41
43
  ConduitString: () => ConduitString,
42
44
  ConfigController: () => ConfigController,
45
+ DEFAULT_LEEWAY_SECONDS: () => DEFAULT_LEEWAY_SECONDS,
43
46
  DEFAULT_STRIP_FIELDS: () => DEFAULT_STRIP_FIELDS,
44
47
  GrpcServer: () => GrpcServer,
45
48
  ManagedModule: () => ManagedModule,
@@ -52,20 +55,27 @@ __export(index_exports, {
52
55
  convictConfigParser: () => convictConfigParser,
53
56
  createParsedRouterRequest: () => createParsedRouterRequest,
54
57
  createServer: () => createServer,
58
+ createUserHydrator: () => createUserHydrator,
55
59
  exportModel: () => exportModel,
60
+ getAuthMiddlewareKind: () => getAuthMiddlewareKind,
61
+ hydrateContextUser: () => hydrateContextUser,
56
62
  importModel: () => importModel,
57
63
  initializeSdk: () => initializeSdk,
58
64
  linearBackoffTimeout: () => linearBackoffTimeout,
59
65
  linearBackoffTimeoutAsync: () => linearBackoffTimeoutAsync,
66
+ mapHydrationErrorCode: () => mapHydrationErrorCode,
60
67
  merge: () => merge,
61
68
  readConduitPeersManifest: () => readConduitPeersManifest,
62
69
  reconcileStoredModuleConfig: () => reconcileStoredModuleConfig,
63
70
  redactSensitiveConfig: () => redactSensitiveConfig,
64
71
  restoreRedactedSecrets: () => restoreRedactedSecrets,
65
72
  sanitizeDocumentsForExport: () => sanitizeDocumentsForExport,
73
+ selectVerificationKey: () => selectVerificationKey,
74
+ shouldHydrateUser: () => shouldHydrateUser,
66
75
  stableConfigJson: () => stableConfigJson,
67
76
  storedConfigsEquivalent: () => storedConfigsEquivalent,
68
77
  stripInternalFields: () => stripInternalFields,
78
+ verifyAccessToken: () => verifyAccessToken,
69
79
  wrapFunctionsAsync: () => wrapFunctionsAsync,
70
80
  wrapGrpcFunctions: () => wrapGrpcFunctions,
71
81
  wrapRouterGrpcFunction: () => wrapRouterGrpcFunction
@@ -461,8 +471,8 @@ var GrpcServer = class {
461
471
  var import_path = __toESM(require("path"), 1);
462
472
  var import_events = require("events");
463
473
  var import_lodash_es2 = require("lodash-es");
464
- var import_grpc_js4 = require("@grpc/grpc-js");
465
- var import_grpc_sdk8 = require("@conduitplatform/grpc-sdk");
474
+ var import_grpc_js6 = require("@grpc/grpc-js");
475
+ var import_grpc_sdk9 = require("@conduitplatform/grpc-sdk");
466
476
 
467
477
  // src/routing/RouteBuilder.ts
468
478
  var import_grpc_sdk5 = require("@conduitplatform/grpc-sdk");
@@ -540,6 +550,16 @@ var RouteBuilder = class {
540
550
  this._options.captureRawHeaders = capture;
541
551
  return this;
542
552
  }
553
+ /**
554
+ * Whether module-tools hydrates `context.user` from `context.userId` on routes with
555
+ * `authMiddleware` / `authMiddleware?` (default true).
556
+ * @deprecated The default flips to false in the next major; call
557
+ * `grpcSdk.authentication.getUser(userId)` explicitly instead.
558
+ */
559
+ hydrateUser(hydrate) {
560
+ this._options.hydrateUser = hydrate;
561
+ return this;
562
+ }
543
563
  return(name, fields) {
544
564
  this._returns = new import_grpc_sdk5.ConduitRouteReturnDefinition(name, fields);
545
565
  return this;
@@ -572,44 +592,104 @@ var RouteBuilder = class {
572
592
  };
573
593
 
574
594
  // src/routing/wrapRouterFunctions.ts
595
+ var import_grpc_sdk7 = require("@conduitplatform/grpc-sdk");
596
+ var import_grpc_js4 = require("@grpc/grpc-js");
597
+
598
+ // src/routing/hydrateUser.ts
575
599
  var import_grpc_sdk6 = require("@conduitplatform/grpc-sdk");
576
600
  var import_grpc_js3 = require("@grpc/grpc-js");
601
+ function middlewareList(routeOptions) {
602
+ const m = routeOptions?.middlewares;
603
+ if (Array.isArray(m)) return m.filter((x) => typeof x === "string");
604
+ if (typeof m === "string") return [m];
605
+ return [];
606
+ }
607
+ function getAuthMiddlewareKind(routeOptions) {
608
+ const list = middlewareList(routeOptions);
609
+ if (list.includes("authMiddleware")) return "required";
610
+ if (list.includes("authMiddleware?")) return "optional";
611
+ return void 0;
612
+ }
613
+ function shouldHydrateUser(routeOptions, context) {
614
+ if (!routeOptions || !context) return false;
615
+ if (routeOptions.hydrateUser === false) return false;
616
+ if (!getAuthMiddlewareKind(routeOptions)) return false;
617
+ return typeof context.userId === "string" && !!context.userId && !context.user;
618
+ }
619
+ function createUserHydrator(routeOptions, resolve) {
620
+ if (!routeOptions) return void 0;
621
+ const kind = getAuthMiddlewareKind(routeOptions);
622
+ if (!kind || routeOptions.hydrateUser === false) return void 0;
623
+ return {
624
+ optional: kind === "optional",
625
+ getUser: async (userId) => {
626
+ const resolver = resolve();
627
+ if (!resolver) throw new import_grpc_sdk6.GrpcError(import_grpc_js3.status.UNAVAILABLE, "User lookup unavailable");
628
+ return resolver(userId);
629
+ }
630
+ };
631
+ }
632
+ function mapHydrationErrorCode(error) {
633
+ const code = error?.code;
634
+ if (typeof code !== "number" || code === import_grpc_js3.status.OK) return import_grpc_js3.status.INTERNAL;
635
+ if (code === import_grpc_js3.status.NOT_FOUND) return import_grpc_js3.status.UNAUTHENTICATED;
636
+ return code;
637
+ }
638
+ async function hydrateContextUser(routeOptions, context, hydrator) {
639
+ if (!shouldHydrateUser(routeOptions, context)) return;
640
+ try {
641
+ context.user = await hydrator.getUser(context.userId);
642
+ } catch (e) {
643
+ import_grpc_sdk6.ConduitGrpcSdk.Logger.warn(
644
+ `User hydration failed: ${e?.message ?? String(e)}`
645
+ );
646
+ if (!hydrator.optional) {
647
+ const code = mapHydrationErrorCode(e);
648
+ throw new import_grpc_sdk6.GrpcError(
649
+ code,
650
+ code === import_grpc_js3.status.UNAUTHENTICATED ? "Unauthorized" : "User lookup failed"
651
+ );
652
+ }
653
+ }
654
+ }
655
+
656
+ // src/routing/wrapRouterFunctions.ts
577
657
  var mapGrpcStatusToHttp = (code) => {
578
658
  if (!code) return "200 (OK)";
579
659
  switch (code) {
580
- case import_grpc_js3.status.OK:
660
+ case import_grpc_js4.status.OK:
581
661
  return `200 (OK)`;
582
- case import_grpc_js3.status.CANCELLED:
662
+ case import_grpc_js4.status.CANCELLED:
583
663
  return `499 (Cancelled)`;
584
- case import_grpc_js3.status.INVALID_ARGUMENT:
664
+ case import_grpc_js4.status.INVALID_ARGUMENT:
585
665
  return `400 (Invalid Argument)`;
586
- case import_grpc_js3.status.DEADLINE_EXCEEDED:
666
+ case import_grpc_js4.status.DEADLINE_EXCEEDED:
587
667
  return `504 (Deadline Exceeded)`;
588
- case import_grpc_js3.status.NOT_FOUND:
668
+ case import_grpc_js4.status.NOT_FOUND:
589
669
  return `404 (Not Found)`;
590
- case import_grpc_js3.status.ALREADY_EXISTS:
670
+ case import_grpc_js4.status.ALREADY_EXISTS:
591
671
  return `409 (Already Exists)`;
592
- case import_grpc_js3.status.PERMISSION_DENIED:
672
+ case import_grpc_js4.status.PERMISSION_DENIED:
593
673
  return `403 (Permission Denied)`;
594
- case import_grpc_js3.status.UNAUTHENTICATED:
674
+ case import_grpc_js4.status.UNAUTHENTICATED:
595
675
  return `401 (Unauthenticated)`;
596
- case import_grpc_js3.status.OUT_OF_RANGE:
676
+ case import_grpc_js4.status.OUT_OF_RANGE:
597
677
  return `416 (Out of Range)`;
598
- case import_grpc_js3.status.FAILED_PRECONDITION:
678
+ case import_grpc_js4.status.FAILED_PRECONDITION:
599
679
  return `412 (Failed Precondition)`;
600
- case import_grpc_js3.status.ABORTED:
680
+ case import_grpc_js4.status.ABORTED:
601
681
  return `409 (Aborted)`;
602
- case import_grpc_js3.status.INTERNAL:
682
+ case import_grpc_js4.status.INTERNAL:
603
683
  return `416 (Internal)`;
604
- case import_grpc_js3.status.UNAVAILABLE:
684
+ case import_grpc_js4.status.UNAVAILABLE:
605
685
  return `503 (Service Unavailable)`;
606
- case import_grpc_js3.status.DATA_LOSS:
686
+ case import_grpc_js4.status.DATA_LOSS:
607
687
  return `500 (Data Loss)`;
608
688
  default:
609
689
  return `500 (Unknown)`;
610
690
  }
611
691
  };
612
- function generateLog(routerType, routerRequest, requestReceive, call, status6) {
692
+ function generateLog(routerType, routerRequest, requestReceive, call, status10) {
613
693
  let log = "";
614
694
  const latency = Date.now() - requestReceive;
615
695
  if (routerRequest) {
@@ -619,15 +699,15 @@ function generateLog(routerType, routerRequest, requestReceive, call, status6) {
619
699
  const socketLabel = socketReq.socketId ?? socketReq.socket;
620
700
  log += `(${routerType}) Socket: ${socketReq.event} socket: ${socketLabel}`;
621
701
  }
622
- log += ` ${mapGrpcStatusToHttp(status6) ?? "200"} ${latency}ms`;
623
- import_grpc_sdk6.ConduitGrpcSdk.Logger.log(log);
624
- import_grpc_sdk6.ConduitGrpcSdk.Metrics?.set("grpc_request_latency_seconds", latency / 1e3);
625
- import_grpc_sdk6.ConduitGrpcSdk.Metrics?.set(
702
+ log += ` ${mapGrpcStatusToHttp(status10) ?? "200"} ${latency}ms`;
703
+ import_grpc_sdk7.ConduitGrpcSdk.Logger.log(log);
704
+ import_grpc_sdk7.ConduitGrpcSdk.Metrics?.set("grpc_request_latency_seconds", latency / 1e3);
705
+ import_grpc_sdk7.ConduitGrpcSdk.Metrics?.set(
626
706
  `${routerType}_grpc_request_latency_seconds`,
627
707
  latency / 1e3
628
708
  );
629
- const successStatus = !status6 || status6.toString().startsWith("2");
630
- import_grpc_sdk6.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_response_statuses_total`, 1, {
709
+ const successStatus = !status10 || status10.toString().startsWith("2");
710
+ import_grpc_sdk7.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_response_statuses_total`, 1, {
631
711
  success: successStatus ? "true" : "false"
632
712
  });
633
713
  }
@@ -643,7 +723,7 @@ function parseResponseData(r, routerRequest, requestReceive, call, callback, res
643
723
  responded.did = true;
644
724
  if (!r) {
645
725
  callback({
646
- code: import_grpc_js3.status.INTERNAL,
726
+ code: import_grpc_js4.status.INTERNAL,
647
727
  message: "Handler did not return a response"
648
728
  });
649
729
  }
@@ -674,11 +754,11 @@ function parseResponseData(r, routerRequest, requestReceive, call, callback, res
674
754
  }
675
755
  generateLog(routerType, routerRequest, requestReceive, call, void 0);
676
756
  }
677
- function wrapRouterGrpcFunction(fun, routerType) {
757
+ function wrapRouterGrpcFunction(fun, routerType, hydration) {
678
758
  return (call, callback) => {
679
759
  const requestReceive = Date.now();
680
760
  let routerRequest = true;
681
- import_grpc_sdk6.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_requests_total`);
761
+ import_grpc_sdk7.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_requests_total`);
682
762
  try {
683
763
  call.request.context = parseRequestData(call.request.context);
684
764
  call.request.params = parseRequestData(call.request.params);
@@ -697,16 +777,16 @@ function wrapRouterGrpcFunction(fun, routerType) {
697
777
  }
698
778
  }
699
779
  } catch (e) {
700
- import_grpc_sdk6.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_errors_total`);
701
- generateLog(routerType, routerRequest, requestReceive, call, import_grpc_js3.status.INTERNAL);
702
- import_grpc_sdk6.ConduitGrpcSdk.Logger.error(e.message ?? "Something went wrong");
780
+ import_grpc_sdk7.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_errors_total`);
781
+ generateLog(routerType, routerRequest, requestReceive, call, import_grpc_js4.status.INTERNAL);
782
+ import_grpc_sdk7.ConduitGrpcSdk.Logger.error(e.message ?? "Something went wrong");
703
783
  return callback({
704
- code: import_grpc_js3.status.INTERNAL,
784
+ code: import_grpc_js4.status.INTERNAL,
705
785
  message: e.message ?? "Something went wrong"
706
786
  });
707
787
  }
708
788
  const responded = { did: false };
709
- fun(call, (r) => {
789
+ const invoke = () => fun(call, (r) => {
710
790
  parseResponseData(
711
791
  r,
712
792
  routerRequest,
@@ -716,7 +796,13 @@ function wrapRouterGrpcFunction(fun, routerType) {
716
796
  responded,
717
797
  routerType
718
798
  );
719
- }).then(
799
+ });
800
+ const needsHydration = hydration && shouldHydrateUser(hydration.options, call.request.context);
801
+ (needsHydration ? hydrateContextUser(
802
+ hydration.options,
803
+ call.request.context,
804
+ hydration.hydrator
805
+ ).then(invoke) : invoke()).then(
720
806
  (r) => parseResponseData(
721
807
  r,
722
808
  routerRequest,
@@ -727,19 +813,19 @@ function wrapRouterGrpcFunction(fun, routerType) {
727
813
  routerType
728
814
  )
729
815
  ).catch((error) => {
730
- import_grpc_sdk6.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_errors_total`);
816
+ import_grpc_sdk7.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_errors_total`);
731
817
  generateLog(
732
818
  routerType,
733
819
  routerRequest,
734
820
  requestReceive,
735
821
  call,
736
- error.code ?? import_grpc_js3.status.INTERNAL
822
+ error.code ?? import_grpc_js4.status.INTERNAL
737
823
  );
738
- import_grpc_sdk6.ConduitGrpcSdk.Logger.error(
824
+ import_grpc_sdk7.ConduitGrpcSdk.Logger.error(
739
825
  error.debugLogInfo ?? error.message ?? "Something went wrong"
740
826
  );
741
827
  callback({
742
- code: error.code ?? import_grpc_js3.status.INTERNAL,
828
+ code: error.code ?? import_grpc_js4.status.INTERNAL,
743
829
  message: error.details ?? error.message ?? "Something went wrong"
744
830
  });
745
831
  });
@@ -747,21 +833,26 @@ function wrapRouterGrpcFunction(fun, routerType) {
747
833
  }
748
834
 
749
835
  // src/routing/RoutingUtilities.ts
750
- function wrapFunctionsAsync(functions, routerType) {
836
+ function wrapFunctionsAsync(functions, routerType, hydrations = {}) {
751
837
  const modifiedFunctions = {};
752
838
  Object.keys(functions).forEach((key) => {
753
- modifiedFunctions[key] = wrapRouterGrpcFunction(functions[key], routerType);
839
+ modifiedFunctions[key] = wrapRouterGrpcFunction(
840
+ functions[key],
841
+ routerType,
842
+ hydrations[key]
843
+ );
754
844
  });
755
845
  return modifiedFunctions;
756
846
  }
757
847
 
758
848
  // src/routing/RoutingManager.ts
759
- var import_grpc_sdk7 = require("@conduitplatform/grpc-sdk");
849
+ var import_grpc_sdk8 = require("@conduitplatform/grpc-sdk");
850
+ var import_grpc_js5 = require("@grpc/grpc-js");
760
851
  var RoutingManager = class _RoutingManager {
761
852
  constructor(_router, _server) {
762
853
  this._router = _router;
763
854
  this._server = _server;
764
- if (_router instanceof import_grpc_sdk7.Admin) {
855
+ if (_router instanceof import_grpc_sdk8.Admin) {
765
856
  this.isAdmin = true;
766
857
  }
767
858
  }
@@ -770,22 +861,46 @@ var RoutingManager = class _RoutingManager {
770
861
  _moduleRoutes = {};
771
862
  static ClientController;
772
863
  static AdminController;
864
+ /** Set by ManagedModule; used to hydrate context.user on client routes. */
865
+ static grpcSdk;
866
+ static userResolver;
867
+ static warnedNoResolver = false;
868
+ /**
869
+ * Overrides how users are fetched for hydration (default: grpcSdk.authentication.getUser).
870
+ * Intended for the module that owns the users: the authentication module has no gRPC
871
+ * client to itself and will register a local resolver here.
872
+ */
873
+ static setUserResolver(resolver) {
874
+ _RoutingManager.userResolver = resolver;
875
+ }
876
+ static resolveUser() {
877
+ if (_RoutingManager.userResolver) return _RoutingManager.userResolver;
878
+ const sdk = _RoutingManager.grpcSdk;
879
+ if (!sdk) return void 0;
880
+ return async (userId) => {
881
+ const auth = sdk.authentication;
882
+ if (!auth) {
883
+ throw new import_grpc_sdk8.GrpcError(import_grpc_js5.status.UNAVAILABLE, "authentication module unavailable");
884
+ }
885
+ return auth.getUser(userId);
886
+ };
887
+ }
773
888
  _routeHandlers = {};
774
889
  isAdmin = false;
775
890
  get(path4) {
776
- return new RouteBuilder(this).method(import_grpc_sdk7.ConduitRouteActions.GET).path(path4);
891
+ return new RouteBuilder(this).method(import_grpc_sdk8.ConduitRouteActions.GET).path(path4);
777
892
  }
778
893
  post(path4) {
779
- return new RouteBuilder(this).method(import_grpc_sdk7.ConduitRouteActions.POST).path(path4);
894
+ return new RouteBuilder(this).method(import_grpc_sdk8.ConduitRouteActions.POST).path(path4);
780
895
  }
781
896
  delete(path4) {
782
- return new RouteBuilder(this).method(import_grpc_sdk7.ConduitRouteActions.DELETE).path(path4);
897
+ return new RouteBuilder(this).method(import_grpc_sdk8.ConduitRouteActions.DELETE).path(path4);
783
898
  }
784
899
  update(path4) {
785
- return new RouteBuilder(this).method(import_grpc_sdk7.ConduitRouteActions.UPDATE).path(path4);
900
+ return new RouteBuilder(this).method(import_grpc_sdk8.ConduitRouteActions.UPDATE).path(path4);
786
901
  }
787
902
  patch(path4) {
788
- return new RouteBuilder(this).method(import_grpc_sdk7.ConduitRouteActions.PATCH).path(path4);
903
+ return new RouteBuilder(this).method(import_grpc_sdk8.ConduitRouteActions.PATCH).path(path4);
789
904
  }
790
905
  clear() {
791
906
  this._moduleRoutes = {};
@@ -836,7 +951,27 @@ var RoutingManager = class _RoutingManager {
836
951
  }
837
952
  async registerRoutes() {
838
953
  if (Object.keys(this._routeHandlers).length === 0) return;
839
- const modifiedFunctions = wrapFunctionsAsync(this._routeHandlers, this.isAdmin ? "admin" : "client");
954
+ const hydrations = {};
955
+ if (!this.isAdmin) {
956
+ for (const [name, route] of Object.entries(this._moduleRoutes)) {
957
+ const options = route.options;
958
+ const hydrator = createUserHydrator(options, _RoutingManager.resolveUser);
959
+ if (!hydrator) continue;
960
+ const names = "events" in route ? Object.keys(JSON.parse(route.events || "{}")) : [name];
961
+ for (const n of names) hydrations[n] = { options, hydrator };
962
+ if (!_RoutingManager.grpcSdk && !_RoutingManager.userResolver && !_RoutingManager.warnedNoResolver) {
963
+ _RoutingManager.warnedNoResolver = true;
964
+ import_grpc_sdk8.ConduitGrpcSdk.Logger.warn(
965
+ "User hydration is needed but RoutingManager.grpcSdk is not set; hydration will fail"
966
+ );
967
+ }
968
+ }
969
+ }
970
+ const modifiedFunctions = wrapFunctionsAsync(
971
+ this._routeHandlers,
972
+ this.isAdmin ? "admin" : "client",
973
+ hydrations
974
+ );
840
975
  if (this.isAdmin) {
841
976
  _RoutingManager.AdminController.setRoutes(modifiedFunctions);
842
977
  } else {
@@ -882,7 +1017,7 @@ var ConduitServiceModule = class {
882
1017
  // external address:port of service (LoadBalancer)
883
1018
  grpcServer;
884
1019
  events = new import_events.EventEmitter();
885
- _serviceHealthState = import_grpc_sdk8.HealthCheckStatus.SERVING;
1020
+ _serviceHealthState = import_grpc_sdk9.HealthCheckStatus.SERVING;
886
1021
  // default for health-agnostic modules
887
1022
  constructor(moduleName) {
888
1023
  this._moduleName = (0, import_lodash_es2.camelCase)(moduleName);
@@ -909,15 +1044,15 @@ var ConduitServiceModule = class {
909
1044
  return this._serviceHealthState;
910
1045
  }
911
1046
  updateHealth(state, init = false) {
912
- if (state === import_grpc_sdk8.HealthCheckStatus.UNKNOWN && !init || state === import_grpc_sdk8.HealthCheckStatus.SERVICE_UNKNOWN) {
1047
+ if (state === import_grpc_sdk9.HealthCheckStatus.UNKNOWN && !init || state === import_grpc_sdk9.HealthCheckStatus.SERVICE_UNKNOWN) {
913
1048
  throw new Error(
914
- `Cannot explicitly set gRPC health state to ${import_grpc_sdk8.HealthCheckStatus[state]}`
1049
+ `Cannot explicitly set gRPC health state to ${import_grpc_sdk9.HealthCheckStatus[state]}`
915
1050
  );
916
1051
  }
917
1052
  if (!init) {
918
- import_grpc_sdk8.ConduitGrpcSdk.Metrics?.set(
1053
+ import_grpc_sdk9.ConduitGrpcSdk.Metrics?.set(
919
1054
  "module_health_state",
920
- state === import_grpc_sdk8.HealthCheckStatus.SERVING ? 1 : 0
1055
+ state === import_grpc_sdk9.HealthCheckStatus.SERVING ? 1 : 0
921
1056
  );
922
1057
  }
923
1058
  if (init) {
@@ -936,12 +1071,12 @@ var ConduitServiceModule = class {
936
1071
  const service = call.request.service.substring(call.request.service.indexOf(".") + 1);
937
1072
  if (service && service !== this._serviceName) {
938
1073
  callback(null, {
939
- status: import_grpc_sdk8.HealthCheckStatus.SERVICE_UNKNOWN
1074
+ status: import_grpc_sdk9.HealthCheckStatus.SERVICE_UNKNOWN
940
1075
  });
941
1076
  } else {
942
1077
  if (!this._registered) {
943
1078
  return callback(null, {
944
- status: import_grpc_sdk8.HealthCheckResponse_ServingStatus.UNKNOWN
1079
+ status: import_grpc_sdk9.HealthCheckResponse_ServingStatus.UNKNOWN
945
1080
  });
946
1081
  }
947
1082
  callback(null, {
@@ -953,14 +1088,14 @@ var ConduitServiceModule = class {
953
1088
  const service = call.request.service.substring(call.request.service.indexOf(".") + 1);
954
1089
  if (service && service !== this._serviceName) {
955
1090
  call.write({
956
- status: import_grpc_sdk8.HealthCheckStatus.SERVICE_UNKNOWN
1091
+ status: import_grpc_sdk9.HealthCheckStatus.SERVICE_UNKNOWN
957
1092
  });
958
1093
  } else {
959
1094
  this.events.on(
960
1095
  `grpc-health-change:${this._serviceName}`,
961
- (status6) => {
1096
+ (status10) => {
962
1097
  call.write({
963
- status: status6
1098
+ status: status10
964
1099
  });
965
1100
  }
966
1101
  );
@@ -983,7 +1118,7 @@ var ConduitServiceModule = class {
983
1118
  const resources = this.getExportableResources();
984
1119
  callback(null, { resources: JSON.stringify(resources) });
985
1120
  } catch (e) {
986
- callback({ code: import_grpc_js4.status.INTERNAL, message: e.message });
1121
+ callback({ code: import_grpc_js6.status.INTERNAL, message: e.message });
987
1122
  }
988
1123
  }
989
1124
  async exportResourcesHandler(call, callback) {
@@ -992,7 +1127,7 @@ var ConduitServiceModule = class {
992
1127
  const data = await this.exportResources(resourceTypes);
993
1128
  callback(null, { data: JSON.stringify(data) });
994
1129
  } catch (e) {
995
- callback({ code: import_grpc_js4.status.INTERNAL, message: e.message });
1130
+ callback({ code: import_grpc_js6.status.INTERNAL, message: e.message });
996
1131
  }
997
1132
  }
998
1133
  async importResourcesHandler(call, callback) {
@@ -1004,11 +1139,11 @@ var ConduitServiceModule = class {
1004
1139
  const err = e;
1005
1140
  if (err instanceof SyntaxError) {
1006
1141
  callback({
1007
- code: import_grpc_js4.status.INVALID_ARGUMENT,
1142
+ code: import_grpc_js6.status.INVALID_ARGUMENT,
1008
1143
  message: "Invalid JSON in import data"
1009
1144
  });
1010
1145
  } else {
1011
- callback({ code: import_grpc_js4.status.INTERNAL, message: err.message });
1146
+ callback({ code: import_grpc_js6.status.INTERNAL, message: err.message });
1012
1147
  }
1013
1148
  }
1014
1149
  }
@@ -1081,7 +1216,7 @@ var ConfigController = class _ConfigController {
1081
1216
 
1082
1217
  // src/ManagedModule.ts
1083
1218
  var import_lodash_es6 = require("lodash-es");
1084
- var import_grpc_js6 = require("@grpc/grpc-js");
1219
+ var import_grpc_js10 = require("@grpc/grpc-js");
1085
1220
  var import_convict = __toESM(require("convict"), 1);
1086
1221
 
1087
1222
  // src/interfaces/ModuleLifecycleStage.ts
@@ -1095,13 +1230,13 @@ var ModuleLifecycleStage = /* @__PURE__ */ ((ModuleLifecycleStage2) => {
1095
1230
  })(ModuleLifecycleStage || {});
1096
1231
 
1097
1232
  // src/ManagedModule.ts
1098
- var import_grpc_sdk14 = require("@conduitplatform/grpc-sdk");
1233
+ var import_grpc_sdk17 = require("@conduitplatform/grpc-sdk");
1099
1234
 
1100
1235
  // src/utilities/initializeSdk.ts
1101
- var import_grpc_sdk13 = require("@conduitplatform/grpc-sdk");
1236
+ var import_grpc_sdk14 = require("@conduitplatform/grpc-sdk");
1102
1237
 
1103
1238
  // src/logging/index.ts
1104
- var import_grpc_sdk9 = require("@conduitplatform/grpc-sdk");
1239
+ var import_grpc_sdk10 = require("@conduitplatform/grpc-sdk");
1105
1240
  var import_winston = __toESM(require("winston"), 1);
1106
1241
  var import_lodash_es3 = require("lodash-es");
1107
1242
  var import_http = require("http");
@@ -1258,7 +1393,7 @@ async function setupLoki(module2, instance) {
1258
1393
  if (lokiUrl.endsWith("/")) lokiUrl = lokiUrl.slice(0, -1);
1259
1394
  const onTry = async () => {
1260
1395
  return await lokiReadyCheck(lokiUrl).then(() => {
1261
- import_grpc_sdk9.ConduitGrpcSdk.Logger.addTransport(
1396
+ import_grpc_sdk10.ConduitGrpcSdk.Logger.addTransport(
1262
1397
  new import_winston_loki.default({
1263
1398
  level: "debug",
1264
1399
  host: lokiUrl,
@@ -1277,7 +1412,7 @@ async function setupLoki(module2, instance) {
1277
1412
  }).catch(() => true);
1278
1413
  };
1279
1414
  const onFailure = () => {
1280
- import_grpc_sdk9.ConduitGrpcSdk.Logger.error(`Failed to connect to Loki on '${lokiUrl}'`);
1415
+ import_grpc_sdk10.ConduitGrpcSdk.Logger.error(`Failed to connect to Loki on '${lokiUrl}'`);
1281
1416
  };
1282
1417
  await linearBackoffTimeoutAsync(onTry, 250, 15, onFailure, true);
1283
1418
  }
@@ -1368,7 +1503,7 @@ var import_prom_client = require("prom-client");
1368
1503
  // src/metrics/MetricsServer.ts
1369
1504
  var import_http2 = require("http");
1370
1505
  var import_lodash_es4 = require("lodash-es");
1371
- var import_grpc_sdk10 = require("@conduitplatform/grpc-sdk");
1506
+ var import_grpc_sdk11 = require("@conduitplatform/grpc-sdk");
1372
1507
  var MetricsServer = class {
1373
1508
  moduleName;
1374
1509
  instance;
@@ -1383,7 +1518,7 @@ var MetricsServer = class {
1383
1518
  const url = "0.0.0.0:" + port.toString();
1384
1519
  const server = (0, import_http2.createServer)(this.requestHandler.bind(this));
1385
1520
  server.listen(port, () => {
1386
- import_grpc_sdk10.ConduitGrpcSdk.Logger.info(`Metrics server listening on: ${url}`);
1521
+ import_grpc_sdk11.ConduitGrpcSdk.Logger.info(`Metrics server listening on: ${url}`);
1387
1522
  });
1388
1523
  return server;
1389
1524
  }
@@ -1405,12 +1540,12 @@ var MetricsServer = class {
1405
1540
  getHttpPort() {
1406
1541
  const value = process.env["METRICS_PORT"];
1407
1542
  if (!value) {
1408
- import_grpc_sdk10.ConduitGrpcSdk.Logger.error(`Metrics HTTP port not set`);
1543
+ import_grpc_sdk11.ConduitGrpcSdk.Logger.error(`Metrics HTTP port not set`);
1409
1544
  process.exit(-1);
1410
1545
  }
1411
1546
  const port = parseInt(value, 10);
1412
1547
  if ((0, import_lodash_es4.isNaN)(port) || port < 1 || port > 65535) {
1413
- import_grpc_sdk10.ConduitGrpcSdk.Logger.error(`Invalid HTTP port value: ${port}`);
1548
+ import_grpc_sdk11.ConduitGrpcSdk.Logger.error(`Invalid HTTP port value: ${port}`);
1414
1549
  process.exit(-1);
1415
1550
  }
1416
1551
  return port;
@@ -1418,73 +1553,73 @@ var MetricsServer = class {
1418
1553
  };
1419
1554
 
1420
1555
  // src/metrics/config/defaults.ts
1421
- var import_grpc_sdk11 = require("@conduitplatform/grpc-sdk");
1556
+ var import_grpc_sdk12 = require("@conduitplatform/grpc-sdk");
1422
1557
  var defaults_default = {
1423
1558
  internalGrpcRequests: {
1424
- type: import_grpc_sdk11.MetricType.Counter,
1559
+ type: import_grpc_sdk12.MetricType.Counter,
1425
1560
  config: {
1426
1561
  name: "internal_grpc_requests_total",
1427
1562
  help: "Tracks the total number of Conduit's internal gRPC requests"
1428
1563
  }
1429
1564
  },
1430
1565
  clientGrpcRequests: {
1431
- type: import_grpc_sdk11.MetricType.Counter,
1566
+ type: import_grpc_sdk12.MetricType.Counter,
1432
1567
  config: {
1433
1568
  name: "client_grpc_requests_total",
1434
1569
  help: "Tracks the total number of the client gRPC requests"
1435
1570
  }
1436
1571
  },
1437
1572
  adminGrpcRequests: {
1438
- type: import_grpc_sdk11.MetricType.Counter,
1573
+ type: import_grpc_sdk12.MetricType.Counter,
1439
1574
  config: {
1440
1575
  name: "admin_grpc_requests_total",
1441
1576
  help: "Tracks the total number of the admin gRPC requests"
1442
1577
  }
1443
1578
  },
1444
1579
  clientGrpcErrors: {
1445
- type: import_grpc_sdk11.MetricType.Counter,
1580
+ type: import_grpc_sdk12.MetricType.Counter,
1446
1581
  config: {
1447
1582
  name: "client_grpc_errors_total",
1448
1583
  help: "Tracks the total number of client grpc errors"
1449
1584
  }
1450
1585
  },
1451
1586
  adminGrpcErrors: {
1452
- type: import_grpc_sdk11.MetricType.Counter,
1587
+ type: import_grpc_sdk12.MetricType.Counter,
1453
1588
  config: {
1454
1589
  name: "admin_grpc_errors_total",
1455
1590
  help: "Tracks the total number of admin grpc errors"
1456
1591
  }
1457
1592
  },
1458
1593
  moduleHealthState: {
1459
- type: import_grpc_sdk11.MetricType.Gauge,
1594
+ type: import_grpc_sdk12.MetricType.Gauge,
1460
1595
  config: {
1461
1596
  name: "module_health_state",
1462
1597
  help: "Tracks the health state of the module"
1463
1598
  }
1464
1599
  },
1465
1600
  grpcRequestLatency: {
1466
- type: import_grpc_sdk11.MetricType.Gauge,
1601
+ type: import_grpc_sdk12.MetricType.Gauge,
1467
1602
  config: {
1468
1603
  name: "grpc_request_latency_seconds",
1469
1604
  help: "Tracks the latency of grpc requests in seconds"
1470
1605
  }
1471
1606
  },
1472
1607
  adminGrpcRequestLatency: {
1473
- type: import_grpc_sdk11.MetricType.Gauge,
1608
+ type: import_grpc_sdk12.MetricType.Gauge,
1474
1609
  config: {
1475
1610
  name: "admin_grpc_request_latency_seconds",
1476
1611
  help: "Tracks the latency of admin grpc requests in seconds"
1477
1612
  }
1478
1613
  },
1479
1614
  clientGrpcRequestLatency: {
1480
- type: import_grpc_sdk11.MetricType.Gauge,
1615
+ type: import_grpc_sdk12.MetricType.Gauge,
1481
1616
  config: {
1482
1617
  name: "client_grpc_request_latency_seconds",
1483
1618
  help: "Tracks the latency of client grpc requests in seconds"
1484
1619
  }
1485
1620
  },
1486
1621
  grpcResponseStatuses: {
1487
- type: import_grpc_sdk11.MetricType.Counter,
1622
+ type: import_grpc_sdk12.MetricType.Counter,
1488
1623
  config: {
1489
1624
  name: "grpc_response_statuses_total",
1490
1625
  help: "Tracks the total number of grpc response codes",
@@ -1492,7 +1627,7 @@ var defaults_default = {
1492
1627
  }
1493
1628
  },
1494
1629
  adminGrpcResponseStatuses: {
1495
- type: import_grpc_sdk11.MetricType.Counter,
1630
+ type: import_grpc_sdk12.MetricType.Counter,
1496
1631
  config: {
1497
1632
  name: "admin_grpc_response_statuses_total",
1498
1633
  help: "Tracks the total number of admin grpc response codes",
@@ -1500,7 +1635,7 @@ var defaults_default = {
1500
1635
  }
1501
1636
  },
1502
1637
  clientGrpcResponseStatuses: {
1503
- type: import_grpc_sdk11.MetricType.Counter,
1638
+ type: import_grpc_sdk12.MetricType.Counter,
1504
1639
  config: {
1505
1640
  name: "client_grpc_response_statuses_total",
1506
1641
  help: "Tracks the total number of client grpc response codes",
@@ -1510,7 +1645,7 @@ var defaults_default = {
1510
1645
  };
1511
1646
 
1512
1647
  // src/metrics/index.ts
1513
- var import_grpc_sdk12 = require("@conduitplatform/grpc-sdk");
1648
+ var import_grpc_sdk13 = require("@conduitplatform/grpc-sdk");
1514
1649
  var METRICS_PREFIX = "cnd_";
1515
1650
  var ConduitMetrics = class {
1516
1651
  moduleName;
@@ -1542,16 +1677,16 @@ var ConduitMetrics = class {
1542
1677
  const metricConfig = JSON.parse(JSON.stringify(config));
1543
1678
  metricConfig.name = this.addPrefix(config.name);
1544
1679
  switch (type) {
1545
- case import_grpc_sdk12.MetricType.Counter:
1680
+ case import_grpc_sdk13.MetricType.Counter:
1546
1681
  this.createCounter(metricConfig);
1547
1682
  break;
1548
- case import_grpc_sdk12.MetricType.Gauge:
1683
+ case import_grpc_sdk13.MetricType.Gauge:
1549
1684
  this.createGauge(metricConfig);
1550
1685
  break;
1551
- case import_grpc_sdk12.MetricType.Histogram:
1686
+ case import_grpc_sdk13.MetricType.Histogram:
1552
1687
  this.createHistogram(metricConfig);
1553
1688
  break;
1554
- case import_grpc_sdk12.MetricType.Summary:
1689
+ case import_grpc_sdk13.MetricType.Summary:
1555
1690
  this.createSummary(metricConfig);
1556
1691
  break;
1557
1692
  }
@@ -1699,7 +1834,7 @@ function clientMiddleware() {
1699
1834
  clientStartedMetric.inc(labels);
1700
1835
  const stopTimer = clientHandlingSecondsMetric.startTimer(labels);
1701
1836
  let settled = false;
1702
- let status6 = import_nice_grpc_common.Status.OK;
1837
+ let status10 = import_nice_grpc_common.Status.OK;
1703
1838
  try {
1704
1839
  let request;
1705
1840
  if (!call.requestStream) {
@@ -1725,28 +1860,28 @@ function clientMiddleware() {
1725
1860
  } catch (err) {
1726
1861
  settled = true;
1727
1862
  if (err instanceof import_nice_grpc_common.ClientError) {
1728
- status6 = err.code;
1863
+ status10 = err.code;
1729
1864
  } else if ((0, import_abort_controller_x.isAbortError)(err)) {
1730
- status6 = import_nice_grpc_common.Status.ABORTED;
1865
+ status10 = import_nice_grpc_common.Status.ABORTED;
1731
1866
  } else {
1732
- status6 = import_nice_grpc_common.Status.UNKNOWN;
1867
+ status10 = import_nice_grpc_common.Status.UNKNOWN;
1733
1868
  }
1734
1869
  throw err;
1735
1870
  } finally {
1736
1871
  if (!settled) {
1737
- status6 = import_nice_grpc_common.Status.CANCELLED;
1872
+ status10 = import_nice_grpc_common.Status.CANCELLED;
1738
1873
  }
1739
- stopTimer({ [codeLabel]: import_nice_grpc_common.Status[status6] });
1874
+ stopTimer({ [codeLabel]: import_nice_grpc_common.Status[status10] });
1740
1875
  clientHandledMetric.inc({
1741
1876
  ...labels,
1742
- [codeLabel]: import_nice_grpc_common.Status[status6]
1877
+ [codeLabel]: import_nice_grpc_common.Status[status10]
1743
1878
  });
1744
1879
  }
1745
1880
  };
1746
1881
  }
1747
1882
 
1748
1883
  // src/utilities/initializeSdk.ts
1749
- var initializeSdk = (coreUrl, moduleName, watchModules = true, serviceHealthStatusGetter = () => import_grpc_sdk13.HealthCheckStatus.SERVING) => {
1884
+ var initializeSdk = (coreUrl, moduleName, watchModules = true, serviceHealthStatusGetter = () => import_grpc_sdk14.HealthCheckStatus.SERVING) => {
1750
1885
  let logger;
1751
1886
  try {
1752
1887
  const fileTransports = createFileLogTransports();
@@ -1754,7 +1889,7 @@ var initializeSdk = (coreUrl, moduleName, watchModules = true, serviceHealthStat
1754
1889
  } catch {
1755
1890
  logger = new ConduitLogger();
1756
1891
  }
1757
- const grpcSdk = new import_grpc_sdk13.ConduitGrpcSdk(
1892
+ const grpcSdk = new import_grpc_sdk14.ConduitGrpcSdk(
1758
1893
  coreUrl,
1759
1894
  moduleName,
1760
1895
  watchModules,
@@ -1763,7 +1898,7 @@ var initializeSdk = (coreUrl, moduleName, watchModules = true, serviceHealthStat
1763
1898
  );
1764
1899
  setupLoki(grpcSdk.name, grpcSdk.instance).then();
1765
1900
  if (process.env.METRICS_PORT) {
1766
- import_grpc_sdk13.ConduitGrpcSdk.Metrics = new ConduitMetrics(grpcSdk.name, grpcSdk.instance);
1901
+ import_grpc_sdk14.ConduitGrpcSdk.Metrics = new ConduitMetrics(grpcSdk.name, grpcSdk.instance);
1767
1902
  grpcSdk.addMiddleware(clientMiddleware());
1768
1903
  }
1769
1904
  return grpcSdk;
@@ -1961,8 +2096,346 @@ async function reconcileStoredModuleConfig(args) {
1961
2096
  return { config: persisted, persisted: true };
1962
2097
  }
1963
2098
 
2099
+ // src/utilities/accessTokenVerifier.ts
2100
+ var import_grpc_sdk16 = require("@conduitplatform/grpc-sdk");
2101
+ var import_grpc_js8 = require("@grpc/grpc-js");
2102
+ var import_node_crypto = require("crypto");
2103
+
2104
+ // src/utilities/accessTokenVerifier.logic.ts
2105
+ var import_fast_jwt2 = require("fast-jwt");
2106
+ var import_grpc_js7 = require("@grpc/grpc-js");
2107
+ var import_grpc_sdk15 = require("@conduitplatform/grpc-sdk");
2108
+ var DEFAULT_LEEWAY_SECONDS = 5;
2109
+ var verifiers = /* @__PURE__ */ new Map();
2110
+ var decode = (0, import_fast_jwt2.createDecoder)({ complete: true });
2111
+ function fail(reason) {
2112
+ throw new import_grpc_sdk15.GrpcError(import_grpc_js7.status.UNAUTHENTICATED, reason);
2113
+ }
2114
+ function getVerifier(publicKey) {
2115
+ let verifier = verifiers.get(publicKey);
2116
+ if (!verifier) {
2117
+ verifier = (0, import_fast_jwt2.createVerifier)({
2118
+ key: publicKey,
2119
+ algorithms: ["EdDSA"],
2120
+ ignoreExpiration: true,
2121
+ ignoreNotBefore: true,
2122
+ cache: false
2123
+ });
2124
+ verifiers.set(publicKey, verifier);
2125
+ }
2126
+ return verifier;
2127
+ }
2128
+ function selectVerificationKey(kid, signing, now) {
2129
+ if (!kid) return void 0;
2130
+ if (signing.publicKey && kid === signing.kid) return signing.publicKey;
2131
+ if (signing.previousKid && signing.previousPublicKey && kid === signing.previousKid && (signing.previousRetiredAt ?? 0) > now) {
2132
+ return signing.previousPublicKey;
2133
+ }
2134
+ return void 0;
2135
+ }
2136
+ function verifyAccessToken(token, state, options = {}) {
2137
+ const now = options.now ?? Date.now();
2138
+ const leeway = options.leewaySeconds ?? DEFAULT_LEEWAY_SECONDS;
2139
+ if (typeof token !== "string" || token.length === 0) fail("malformed token");
2140
+ let kid;
2141
+ try {
2142
+ const decoded = decode(token);
2143
+ if (decoded.header.alg !== "EdDSA") fail("unsupported algorithm");
2144
+ kid = decoded.header.kid;
2145
+ } catch (e) {
2146
+ if (e instanceof import_grpc_sdk15.GrpcError) throw e;
2147
+ fail("malformed token");
2148
+ }
2149
+ const key = selectVerificationKey(kid, state.signing, now);
2150
+ if (!key) fail("unknown kid");
2151
+ let claims;
2152
+ try {
2153
+ claims = getVerifier(key)(token);
2154
+ } catch {
2155
+ fail("bad signature");
2156
+ }
2157
+ if (typeof claims.sub !== "string" || typeof claims.sid !== "string" || typeof claims.exp !== "number") {
2158
+ fail("malformed token");
2159
+ }
2160
+ if (now / 1e3 >= claims.exp + leeway) fail("expired");
2161
+ if (claims.tv !== state.tokenVersion) fail("version mismatch");
2162
+ if (options.clientId !== void 0 && claims.cid !== options.clientId) {
2163
+ fail("client mismatch");
2164
+ }
2165
+ return claims;
2166
+ }
2167
+
2168
+ // src/utilities/accessTokenVerifier.ts
2169
+ var AUTHENTICATION_CONFIG_UPDATE_CHANNEL = "authentication:config:update";
2170
+ var INITIAL_RETRY_MS = 1e3;
2171
+ var MAX_RETRY_MS = 3e4;
2172
+ var REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
2173
+ var UNKNOWN_KID_REFETCH_MIN_MS = 10 * 1e3;
2174
+ var SUBSCRIBER_ID = "access-token-verifier";
2175
+ function isConfigWithAccessTokens(config) {
2176
+ return typeof config === "object" && config !== null && typeof config.accessTokens === "object" && config.accessTokens !== null;
2177
+ }
2178
+ var AccessTokenVerifier = class _AccessTokenVerifier {
2179
+ constructor(grpcSdk) {
2180
+ this.grpcSdk = grpcSdk;
2181
+ }
2182
+ grpcSdk;
2183
+ static instance;
2184
+ _mode = "session";
2185
+ _strictRevocation = false;
2186
+ _tokenVersion = 0;
2187
+ _signing;
2188
+ _loaded = false;
2189
+ _subscribed = false;
2190
+ _generation = 0;
2191
+ _running = false;
2192
+ _retryTimer;
2193
+ _refreshTimer;
2194
+ _lastRefetch = 0;
2195
+ _refetching = false;
2196
+ _warned = /* @__PURE__ */ new Set();
2197
+ _listeners = /* @__PURE__ */ new Set();
2198
+ static getInstance(grpcSdk) {
2199
+ if (!_AccessTokenVerifier.instance) {
2200
+ _AccessTokenVerifier.instance = new _AccessTokenVerifier(grpcSdk);
2201
+ }
2202
+ return _AccessTokenVerifier.instance;
2203
+ }
2204
+ /**
2205
+ * Internal, for tests only: stops the verifier, clears all state and drops the singleton.
2206
+ */
2207
+ static resetForTests() {
2208
+ _AccessTokenVerifier.instance?.stop();
2209
+ _AccessTokenVerifier.instance?.resetState();
2210
+ _AccessTokenVerifier.instance = void 0;
2211
+ }
2212
+ resetState() {
2213
+ this._mode = "session";
2214
+ this._strictRevocation = false;
2215
+ this._tokenVersion = 0;
2216
+ this._signing = void 0;
2217
+ this._loaded = false;
2218
+ this._lastRefetch = 0;
2219
+ this._warned.clear();
2220
+ this._listeners.clear();
2221
+ }
2222
+ /** Snapshot of the verifier's observable state. */
2223
+ get state() {
2224
+ return {
2225
+ mode: this._mode,
2226
+ strictRevocation: this._strictRevocation,
2227
+ ready: this.ready,
2228
+ tokenVersion: this._tokenVersion,
2229
+ kid: this._signing?.kid
2230
+ };
2231
+ }
2232
+ /**
2233
+ * Registers a listener fired synchronously at the end of every applyConfig that changed
2234
+ * anything (including the first successful load). Returns an unsubscribe function.
2235
+ */
2236
+ onChange(listener) {
2237
+ this._listeners.add(listener);
2238
+ return () => {
2239
+ this._listeners.delete(listener);
2240
+ };
2241
+ }
2242
+ fingerprint() {
2243
+ return JSON.stringify([this._loaded, this._signing, this.state]);
2244
+ }
2245
+ get mode() {
2246
+ return this._mode;
2247
+ }
2248
+ get strictRevocation() {
2249
+ return this._strictRevocation;
2250
+ }
2251
+ /** True once signing keys have been loaded. */
2252
+ get ready() {
2253
+ return !!this._signing?.publicKey;
2254
+ }
2255
+ get tokenVersion() {
2256
+ return this._tokenVersion;
2257
+ }
2258
+ /**
2259
+ * Loads the authentication config, subscribes to its updates and starts a slow periodic
2260
+ * refresh. The first attempt is awaited; if it fails, retries continue in the background
2261
+ * with capped backoff until the config is loaded and the subscription is acknowledged.
2262
+ * Idempotent while running; never throws.
2263
+ */
2264
+ async start() {
2265
+ if (this._running) return;
2266
+ this._running = true;
2267
+ const generation = ++this._generation;
2268
+ await this.attempt(generation, INITIAL_RETRY_MS);
2269
+ if (generation !== this._generation) return;
2270
+ this._refreshTimer = setInterval(() => void this.refetch(true), REFRESH_INTERVAL_MS);
2271
+ this._refreshTimer.unref?.();
2272
+ }
2273
+ stop() {
2274
+ this._running = false;
2275
+ this._generation++;
2276
+ if (this._retryTimer) clearTimeout(this._retryTimer);
2277
+ if (this._refreshTimer) clearInterval(this._refreshTimer);
2278
+ this._retryTimer = void 0;
2279
+ this._refreshTimer = void 0;
2280
+ if (this._subscribed) {
2281
+ try {
2282
+ this.grpcSdk.bus?.unsubscribe(SUBSCRIBER_ID);
2283
+ } catch {
2284
+ }
2285
+ this._subscribed = false;
2286
+ }
2287
+ }
2288
+ async attempt(generation, delay) {
2289
+ if (generation !== this._generation) return;
2290
+ try {
2291
+ await this.subscribe(generation);
2292
+ if (!this._loaded) {
2293
+ this.applyConfig(await this.grpcSdk.config.get("authentication"));
2294
+ }
2295
+ } catch {
2296
+ }
2297
+ if (generation !== this._generation) return;
2298
+ if (this._subscribed && this._loaded) return;
2299
+ this._retryTimer = setTimeout(
2300
+ () => void this.attempt(generation, Math.min(delay * 2, MAX_RETRY_MS)),
2301
+ delay
2302
+ );
2303
+ this._retryTimer.unref?.();
2304
+ }
2305
+ async subscribe(generation) {
2306
+ if (this._subscribed) return;
2307
+ const bus = this.grpcSdk.bus;
2308
+ if (!bus) return;
2309
+ await bus.subscribeAck(
2310
+ AUTHENTICATION_CONFIG_UPDATE_CHANNEL,
2311
+ (message) => {
2312
+ try {
2313
+ this.applyConfig(JSON.parse(message));
2314
+ } catch {
2315
+ }
2316
+ },
2317
+ SUBSCRIBER_ID
2318
+ );
2319
+ if (generation !== this._generation) {
2320
+ bus.unsubscribe(SUBSCRIBER_ID);
2321
+ return;
2322
+ }
2323
+ this._subscribed = true;
2324
+ }
2325
+ /** Refetches the config from the authentication module; failures keep the previous state. */
2326
+ async refetch(force = false) {
2327
+ const now = Date.now();
2328
+ if (this._refetching) return;
2329
+ if (!force && now - this._lastRefetch < UNKNOWN_KID_REFETCH_MIN_MS) return;
2330
+ this._lastRefetch = now;
2331
+ this._refetching = true;
2332
+ try {
2333
+ this.applyConfig(await this.grpcSdk.config.get("authentication"));
2334
+ } catch {
2335
+ } finally {
2336
+ this._refetching = false;
2337
+ }
2338
+ }
2339
+ warnOnce(key, message) {
2340
+ if (this._warned.has(key)) return;
2341
+ this._warned.add(key);
2342
+ import_grpc_sdk16.ConduitGrpcSdk.Logger.warn(`AccessTokenVerifier: ${message}`);
2343
+ }
2344
+ static validPem(pem) {
2345
+ if (typeof pem !== "string" || !pem) return false;
2346
+ try {
2347
+ (0, import_node_crypto.createPublicKey)(pem);
2348
+ return true;
2349
+ } catch {
2350
+ return false;
2351
+ }
2352
+ }
2353
+ /**
2354
+ * Validates and normalizes the signing keys. Returns undefined when no signing block is
2355
+ * configured, null when the public key is invalid (the caller keeps the previous state).
2356
+ */
2357
+ normalizeSigning(s) {
2358
+ if (!(s && typeof s.kid === "string" && s.kid && s.publicKey)) return void 0;
2359
+ if (!_AccessTokenVerifier.validPem(s.publicKey)) {
2360
+ this.warnOnce("publicKey", "invalid signing public key, keeping previous state");
2361
+ return null;
2362
+ }
2363
+ const previousPublicKey = this.validPreviousKey(s.previousPublicKey);
2364
+ return {
2365
+ kid: s.kid,
2366
+ publicKey: s.publicKey,
2367
+ previousKid: previousPublicKey ? s.previousKid || void 0 : void 0,
2368
+ previousPublicKey,
2369
+ previousRetiredAt: Number(s.previousRetiredAt ?? 0)
2370
+ };
2371
+ }
2372
+ validPreviousKey(pem) {
2373
+ if (!pem) return void 0;
2374
+ if (_AccessTokenVerifier.validPem(pem)) return pem;
2375
+ this.warnOnce("previousPublicKey", "invalid previous public key, ignoring it");
2376
+ return void 0;
2377
+ }
2378
+ parseTokenVersion(raw) {
2379
+ const version = Number(raw ?? 0);
2380
+ if (Number.isNaN(version)) {
2381
+ this.warnOnce("tokenVersion", "invalid tokenVersion, all tokens will be rejected");
2382
+ }
2383
+ return version;
2384
+ }
2385
+ notifyListeners() {
2386
+ const state = this.state;
2387
+ for (const listener of [...this._listeners]) {
2388
+ try {
2389
+ listener(state);
2390
+ } catch (e) {
2391
+ import_grpc_sdk16.ConduitGrpcSdk.Logger.warn(
2392
+ `AccessTokenVerifier: change listener failed: ${e?.message}`
2393
+ );
2394
+ }
2395
+ }
2396
+ }
2397
+ /**
2398
+ * Applies an authentication module config object (full config). Invalid input is ignored
2399
+ * and the previous state is kept; invalid keys are logged once.
2400
+ */
2401
+ applyConfig(config) {
2402
+ if (!isConfigWithAccessTokens(config)) return;
2403
+ const tokens = config.accessTokens;
2404
+ const before = this.fingerprint();
2405
+ const signing = this.normalizeSigning(tokens.signing);
2406
+ if (signing === null) return;
2407
+ const version = this.parseTokenVersion(tokens.tokenVersion);
2408
+ this._mode = tokens.mode === "jwt" ? "jwt" : "session";
2409
+ this._tokenVersion = version;
2410
+ this._strictRevocation = tokens.strictRevocation === true;
2411
+ this._signing = signing;
2412
+ this._loaded = true;
2413
+ if (this.fingerprint() !== before) this.notifyListeners();
2414
+ }
2415
+ /**
2416
+ * Verifies an access token, throwing GrpcError(UNAUTHENTICATED) with a short reason on failure.
2417
+ * `options.now` is epoch milliseconds. An `unknown kid` triggers a throttled (at most once
2418
+ * per 10 s) background refetch of the config, to pick up a rotation sooner.
2419
+ */
2420
+ verify(token, options) {
2421
+ if (!this._signing) {
2422
+ throw new import_grpc_sdk16.GrpcError(import_grpc_js8.status.UNAUTHENTICATED, "verifier not ready");
2423
+ }
2424
+ try {
2425
+ return verifyAccessToken(
2426
+ token,
2427
+ { tokenVersion: this._tokenVersion, signing: this._signing },
2428
+ options
2429
+ );
2430
+ } catch (e) {
2431
+ if (e.message === "unknown kid") void this.refetch();
2432
+ throw e;
2433
+ }
2434
+ }
2435
+ };
2436
+
1964
2437
  // src/routing/RoutingController.ts
1965
- var import_grpc_js5 = require("@grpc/grpc-js");
2438
+ var import_grpc_js9 = require("@grpc/grpc-js");
1966
2439
  var RoutingController = class {
1967
2440
  _routeHandlers = {};
1968
2441
  handleRequest(call, callback) {
@@ -1970,7 +2443,7 @@ var RoutingController = class {
1970
2443
  this._routeHandlers[call.request.functionName](call, callback);
1971
2444
  } else {
1972
2445
  callback({
1973
- code: import_grpc_js5.status.NOT_FOUND,
2446
+ code: import_grpc_js9.status.NOT_FOUND,
1974
2447
  message: "Not found"
1975
2448
  });
1976
2449
  }
@@ -2029,13 +2502,13 @@ var ManagedModule = class extends ConduitServiceModule {
2029
2502
  await this.preRegisterLifecycle();
2030
2503
  await this.grpcSdk.config.registerModule(this.name, this.address, this.healthState);
2031
2504
  } catch (err) {
2032
- import_grpc_sdk14.ConduitGrpcSdk.Logger.error("Failed to initialize server");
2033
- import_grpc_sdk14.ConduitGrpcSdk.Logger.error(err);
2505
+ import_grpc_sdk17.ConduitGrpcSdk.Logger.error("Failed to initialize server");
2506
+ import_grpc_sdk17.ConduitGrpcSdk.Logger.error(err);
2034
2507
  process.exit(-1);
2035
2508
  }
2036
2509
  await this.postRegisterLifecycle().catch((err) => {
2037
- import_grpc_sdk14.ConduitGrpcSdk.Logger.error("Failed to activate module");
2038
- import_grpc_sdk14.ConduitGrpcSdk.Logger.error(err);
2510
+ import_grpc_sdk17.ConduitGrpcSdk.Logger.error("Failed to activate module");
2511
+ import_grpc_sdk17.ConduitGrpcSdk.Logger.error(err);
2039
2512
  process.exit(-1);
2040
2513
  });
2041
2514
  }
@@ -2102,11 +2575,11 @@ var ManagedModule = class extends ConduitServiceModule {
2102
2575
  * Registers common and module-specific metric types.
2103
2576
  */
2104
2577
  async registerMetrics() {
2105
- if (import_grpc_sdk14.ConduitGrpcSdk.Metrics) {
2106
- import_grpc_sdk14.ConduitGrpcSdk.Metrics.initializeDefaultMetrics();
2578
+ if (import_grpc_sdk17.ConduitGrpcSdk.Metrics) {
2579
+ import_grpc_sdk17.ConduitGrpcSdk.Metrics.initializeDefaultMetrics();
2107
2580
  if (this.metricsSchema) {
2108
2581
  Object.values(this.metricsSchema).forEach((metric) => {
2109
- import_grpc_sdk14.ConduitGrpcSdk.Metrics.registerMetric(metric.type, metric.config);
2582
+ import_grpc_sdk17.ConduitGrpcSdk.Metrics.registerMetric(metric.type, metric.config);
2110
2583
  });
2111
2584
  }
2112
2585
  }
@@ -2130,7 +2603,7 @@ var ManagedModule = class extends ConduitServiceModule {
2130
2603
  const known = new Set(this.grpcSdk.getKnownModuleNames());
2131
2604
  for (const n of names) {
2132
2605
  if (!known.has(n)) {
2133
- import_grpc_sdk14.ConduitGrpcSdk.Logger.warn(
2606
+ import_grpc_sdk17.ConduitGrpcSdk.Logger.warn(
2134
2607
  `Declared Conduit peer "${n}" is not a known built-in module (see ConduitGrpcSdk.getKnownModuleNames).`
2135
2608
  );
2136
2609
  }
@@ -2191,18 +2664,19 @@ var ManagedModule = class extends ConduitServiceModule {
2191
2664
  this.service.functions
2192
2665
  );
2193
2666
  }
2667
+ RoutingManager.grpcSdk = this.grpcSdk;
2194
2668
  RoutingManager.ClientController = new RoutingController();
2195
2669
  RoutingManager.AdminController = new RoutingController();
2196
2670
  await this.addHealthCheckService();
2197
2671
  await this.addModuleService();
2198
2672
  await this.grpcServer.start();
2199
- import_grpc_sdk14.ConduitGrpcSdk.Logger.log("gRPC server listening on " + this._port);
2673
+ import_grpc_sdk17.ConduitGrpcSdk.Logger.log("gRPC server listening on " + this._port);
2200
2674
  }
2201
2675
  async setConfig(call, callback) {
2202
2676
  try {
2203
2677
  if (!this.config) {
2204
2678
  return callback({
2205
- code: import_grpc_js6.status.INVALID_ARGUMENT,
2679
+ code: import_grpc_js10.status.INVALID_ARGUMENT,
2206
2680
  message: "Module is not configurable"
2207
2681
  });
2208
2682
  }
@@ -2221,7 +2695,7 @@ var ManagedModule = class extends ConduitServiceModule {
2221
2695
  this.config = (0, import_convict.default)(this.configSchema);
2222
2696
  this.config.load(previousConfig);
2223
2697
  return callback({
2224
- code: import_grpc_js6.status.INVALID_ARGUMENT,
2698
+ code: import_grpc_js10.status.INVALID_ARGUMENT,
2225
2699
  message: "Invalid configuration values: " + e.message
2226
2700
  });
2227
2701
  }
@@ -2230,7 +2704,7 @@ var ManagedModule = class extends ConduitServiceModule {
2230
2704
  await this._drainConfigQueue();
2231
2705
  this.grpcSdk.bus?.publish((0, import_lodash_es6.kebabCase)(this.name) + ":config:update", configJson);
2232
2706
  } catch (e) {
2233
- return callback({ code: import_grpc_js6.status.INTERNAL, message: e.message });
2707
+ return callback({ code: import_grpc_js10.status.INTERNAL, message: e.message });
2234
2708
  }
2235
2709
  }
2236
2710
  /** Used to update the module's configuration on initial Redis/DB reconciliation. */
@@ -2310,8 +2784,8 @@ var ManagedModule = class extends ConduitServiceModule {
2310
2784
  };
2311
2785
 
2312
2786
  // src/classes/ModuleError.ts
2313
- var import_grpc_sdk15 = require("@conduitplatform/grpc-sdk");
2314
- var ModuleError = class extends import_grpc_sdk15.GrpcError {
2787
+ var import_grpc_sdk18 = require("@conduitplatform/grpc-sdk");
2788
+ var ModuleError = class extends import_grpc_sdk18.GrpcError {
2315
2789
  debugLogInfo;
2316
2790
  constructor(errorDefinition, debugLogInfo) {
2317
2791
  const { grpcCode, conduitCode, message } = errorDefinition;
@@ -2327,6 +2801,8 @@ var ModuleError = class extends import_grpc_sdk15.GrpcError {
2327
2801
  };
2328
2802
  // Annotate the CommonJS export names for ESM import in node:
2329
2803
  0 && (module.exports = {
2804
+ AUTHENTICATION_CONFIG_UPDATE_CHANNEL,
2805
+ AccessTokenVerifier,
2330
2806
  ConduitActiveSchema,
2331
2807
  ConduitBoolean,
2332
2808
  ConduitDate,
@@ -2337,6 +2813,7 @@ var ModuleError = class extends import_grpc_sdk15.GrpcError {
2337
2813
  ConduitServiceModule,
2338
2814
  ConduitString,
2339
2815
  ConfigController,
2816
+ DEFAULT_LEEWAY_SECONDS,
2340
2817
  DEFAULT_STRIP_FIELDS,
2341
2818
  GrpcServer,
2342
2819
  ManagedModule,
@@ -2349,20 +2826,27 @@ var ModuleError = class extends import_grpc_sdk15.GrpcError {
2349
2826
  convictConfigParser,
2350
2827
  createParsedRouterRequest,
2351
2828
  createServer,
2829
+ createUserHydrator,
2352
2830
  exportModel,
2831
+ getAuthMiddlewareKind,
2832
+ hydrateContextUser,
2353
2833
  importModel,
2354
2834
  initializeSdk,
2355
2835
  linearBackoffTimeout,
2356
2836
  linearBackoffTimeoutAsync,
2837
+ mapHydrationErrorCode,
2357
2838
  merge,
2358
2839
  readConduitPeersManifest,
2359
2840
  reconcileStoredModuleConfig,
2360
2841
  redactSensitiveConfig,
2361
2842
  restoreRedactedSecrets,
2362
2843
  sanitizeDocumentsForExport,
2844
+ selectVerificationKey,
2845
+ shouldHydrateUser,
2363
2846
  stableConfigJson,
2364
2847
  storedConfigsEquivalent,
2365
2848
  stripInternalFields,
2849
+ verifyAccessToken,
2366
2850
  wrapFunctionsAsync,
2367
2851
  wrapGrpcFunctions,
2368
2852
  wrapRouterGrpcFunction