@openhi/constructs 0.0.86 → 0.0.87

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/index.d.mts CHANGED
@@ -474,6 +474,14 @@ declare class CognitoUserPoolKmsKey extends Key {
474
474
  constructor(scope: Construct, props?: KeyProps);
475
475
  }
476
476
 
477
+ /**
478
+ * Lambda used as Cognito Post Authentication trigger.
479
+ */
480
+ declare class PostAuthenticationLambda extends Construct {
481
+ readonly lambda: NodejsFunction;
482
+ constructor(scope: Construct);
483
+ }
484
+
477
485
  /**
478
486
  * Lambda used as Cognito Pre Token Generation trigger.
479
487
  */
@@ -931,6 +939,7 @@ declare class OpenHiAuthService extends OpenHiService {
931
939
  props: OpenHiAuthServiceProps;
932
940
  readonly userPoolKmsKey: IKey;
933
941
  readonly preTokenGenerationLambda: IFunction;
942
+ readonly postAuthenticationLambda: IFunction;
934
943
  readonly userPool: IUserPool;
935
944
  readonly userPoolClient: IUserPoolClient;
936
945
  readonly userPoolDomain: IUserPoolDomain;
@@ -952,12 +961,32 @@ declare class OpenHiAuthService extends OpenHiService {
952
961
  * openhi_* claims to the access token only; trigger version V2_0 may be required.
953
962
  */
954
963
  protected createPreTokenGenerationLambda(): IFunction;
964
+ /**
965
+ * Creates the Post Authentication Lambda (Cognito trigger). Calls
966
+ * AdminUserGlobalSignOut on every sign-in to enforce single-device-per-user
967
+ * sessions per ADR 2026-03-17-01.
968
+ */
969
+ protected createPostAuthenticationLambda(): IFunction;
955
970
  /**
956
971
  * Creates the Cognito User Pool and exports its ID to SSM.
957
972
  * Look up via {@link OpenHiAuthService.userPoolFromConstruct}.
958
973
  * Override to customize.
959
974
  */
960
975
  protected createUserPool(): IUserPool;
976
+ /**
977
+ * Grants the Post Authentication Lambda permission to call
978
+ * `cognito-idp:AdminUserGlobalSignOut`.
979
+ *
980
+ * Scoped via `Stack.of(this).formatArn` rather than `userPool.userPoolArn`
981
+ * because the User Pool registers this Lambda as a Post Authentication
982
+ * trigger, creating the cycle:
983
+ * userPool → lambda (trigger ARN) → role policy → userPool ARN.
984
+ * Using `formatArn` avoids referencing the User Pool resource directly
985
+ * while still scoping to user pools in this account+region. The Lambda
986
+ * is invoked only by Cognito with a Cognito-provided `event.userPoolId`,
987
+ * so the runtime target is constrained by the trigger contract.
988
+ */
989
+ protected grantPostAuthenticationPermissions(): void;
961
990
  /**
962
991
  * Creates the User Pool Client and exports its ID to SSM (AUTH service type).
963
992
  * Look up via {@link OpenHiAuthService.userPoolClientFromConstruct}.
@@ -1219,4 +1248,4 @@ declare class OpenHiGraphqlService extends OpenHiService {
1219
1248
  protected createRootGraphqlApi(): RootGraphqlApi;
1220
1249
  }
1221
1250
 
1222
- export { type BuildParameterNameProps, ChildHostedZone, type ChildHostedZoneProps, CognitoFixtureSeederClient, type CognitoFixtureSeederClientProps, CognitoUserPool, CognitoUserPoolClient, CognitoUserPoolDomain, CognitoUserPoolKmsKey, DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES, DATA_STORE_CHANGE_DETAIL_TYPE, DATA_STORE_CHANGE_EVENT_SOURCE, DataEventBus, DataStoreHistoricalArchive, type DataStoreHistoricalArchiveProps, DataStorePostgresReplica, type DataStorePostgresReplicaProps, DiscoverableStringParameter, type DiscoverableStringParameterProps, DynamoDbDataStore, type DynamoDbDataStoreProps, type FhirCurrentResourceChangeDetail, OpenHiApp, type OpenHiAppProps, OpenHiAuthService, type OpenHiAuthServiceProps, OpenHiDataService, type OpenHiDataServiceProps, OpenHiEnvironment, type OpenHiEnvironmentProps, OpenHiGlobalService, type OpenHiGlobalServiceProps, OpenHiGraphqlService, type OpenHiGraphqlServiceProps, OpenHiRestApiService, type OpenHiRestApiServiceProps, OpenHiService, type OpenHiServiceProps, type OpenHiServiceType, OpenHiStage, type OpenHiStageProps, OpsEventBus, POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME, POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME, POSTGRES_REPLICA_SECRET_ARN_SSM_NAME, PreTokenGenerationLambda, REST_API_BASE_URL_SSM_NAME, RootGraphqlApi, type RootGraphqlApiProps, RootHostedZone, RootHttpApi, type RootHttpApiProps, RootWildcardCertificate, STATIC_HOSTING_SERVICE_TYPE, StaticHosting, type StaticHostingProps, buildFhirCurrentResourceChangeDetail, getDynamoDbDataStoreTableName, getPostgresReplicaSchemaName };
1251
+ export { type BuildParameterNameProps, ChildHostedZone, type ChildHostedZoneProps, CognitoFixtureSeederClient, type CognitoFixtureSeederClientProps, CognitoUserPool, CognitoUserPoolClient, CognitoUserPoolDomain, CognitoUserPoolKmsKey, DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES, DATA_STORE_CHANGE_DETAIL_TYPE, DATA_STORE_CHANGE_EVENT_SOURCE, DataEventBus, DataStoreHistoricalArchive, type DataStoreHistoricalArchiveProps, DataStorePostgresReplica, type DataStorePostgresReplicaProps, DiscoverableStringParameter, type DiscoverableStringParameterProps, DynamoDbDataStore, type DynamoDbDataStoreProps, type FhirCurrentResourceChangeDetail, OpenHiApp, type OpenHiAppProps, OpenHiAuthService, type OpenHiAuthServiceProps, OpenHiDataService, type OpenHiDataServiceProps, OpenHiEnvironment, type OpenHiEnvironmentProps, OpenHiGlobalService, type OpenHiGlobalServiceProps, OpenHiGraphqlService, type OpenHiGraphqlServiceProps, OpenHiRestApiService, type OpenHiRestApiServiceProps, OpenHiService, type OpenHiServiceProps, type OpenHiServiceType, OpenHiStage, type OpenHiStageProps, OpsEventBus, POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME, POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME, POSTGRES_REPLICA_SECRET_ARN_SSM_NAME, PostAuthenticationLambda, PreTokenGenerationLambda, REST_API_BASE_URL_SSM_NAME, RootGraphqlApi, type RootGraphqlApiProps, RootHostedZone, RootHttpApi, type RootHttpApiProps, RootWildcardCertificate, STATIC_HOSTING_SERVICE_TYPE, StaticHosting, type StaticHostingProps, buildFhirCurrentResourceChangeDetail, getDynamoDbDataStoreTableName, getPostgresReplicaSchemaName };
package/lib/index.d.ts CHANGED
@@ -569,6 +569,14 @@ declare class CognitoUserPoolKmsKey extends Key {
569
569
  constructor(scope: Construct, props?: KeyProps);
570
570
  }
571
571
 
572
+ /**
573
+ * Lambda used as Cognito Post Authentication trigger.
574
+ */
575
+ declare class PostAuthenticationLambda extends Construct {
576
+ readonly lambda: NodejsFunction;
577
+ constructor(scope: Construct);
578
+ }
579
+
572
580
  /**
573
581
  * Lambda used as Cognito Pre Token Generation trigger.
574
582
  */
@@ -1026,6 +1034,7 @@ declare class OpenHiAuthService extends OpenHiService {
1026
1034
  props: OpenHiAuthServiceProps;
1027
1035
  readonly userPoolKmsKey: IKey;
1028
1036
  readonly preTokenGenerationLambda: IFunction;
1037
+ readonly postAuthenticationLambda: IFunction;
1029
1038
  readonly userPool: IUserPool;
1030
1039
  readonly userPoolClient: IUserPoolClient;
1031
1040
  readonly userPoolDomain: IUserPoolDomain;
@@ -1047,12 +1056,32 @@ declare class OpenHiAuthService extends OpenHiService {
1047
1056
  * openhi_* claims to the access token only; trigger version V2_0 may be required.
1048
1057
  */
1049
1058
  protected createPreTokenGenerationLambda(): IFunction;
1059
+ /**
1060
+ * Creates the Post Authentication Lambda (Cognito trigger). Calls
1061
+ * AdminUserGlobalSignOut on every sign-in to enforce single-device-per-user
1062
+ * sessions per ADR 2026-03-17-01.
1063
+ */
1064
+ protected createPostAuthenticationLambda(): IFunction;
1050
1065
  /**
1051
1066
  * Creates the Cognito User Pool and exports its ID to SSM.
1052
1067
  * Look up via {@link OpenHiAuthService.userPoolFromConstruct}.
1053
1068
  * Override to customize.
1054
1069
  */
1055
1070
  protected createUserPool(): IUserPool;
1071
+ /**
1072
+ * Grants the Post Authentication Lambda permission to call
1073
+ * `cognito-idp:AdminUserGlobalSignOut`.
1074
+ *
1075
+ * Scoped via `Stack.of(this).formatArn` rather than `userPool.userPoolArn`
1076
+ * because the User Pool registers this Lambda as a Post Authentication
1077
+ * trigger, creating the cycle:
1078
+ * userPool → lambda (trigger ARN) → role policy → userPool ARN.
1079
+ * Using `formatArn` avoids referencing the User Pool resource directly
1080
+ * while still scoping to user pools in this account+region. The Lambda
1081
+ * is invoked only by Cognito with a Cognito-provided `event.userPoolId`,
1082
+ * so the runtime target is constrained by the trigger contract.
1083
+ */
1084
+ protected grantPostAuthenticationPermissions(): void;
1056
1085
  /**
1057
1086
  * Creates the User Pool Client and exports its ID to SSM (AUTH service type).
1058
1087
  * Look up via {@link OpenHiAuthService.userPoolClientFromConstruct}.
@@ -1314,5 +1343,5 @@ declare class OpenHiGraphqlService extends OpenHiService {
1314
1343
  protected createRootGraphqlApi(): RootGraphqlApi;
1315
1344
  }
1316
1345
 
1317
- export { ChildHostedZone, CognitoFixtureSeederClient, CognitoUserPool, CognitoUserPoolClient, CognitoUserPoolDomain, CognitoUserPoolKmsKey, DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES, DATA_STORE_CHANGE_DETAIL_TYPE, DATA_STORE_CHANGE_EVENT_SOURCE, DataEventBus, DataStoreHistoricalArchive, DataStorePostgresReplica, DiscoverableStringParameter, DynamoDbDataStore, OpenHiApp, OpenHiAuthService, OpenHiDataService, OpenHiEnvironment, OpenHiGlobalService, OpenHiGraphqlService, OpenHiRestApiService, OpenHiService, OpenHiStage, OpsEventBus, POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME, POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME, POSTGRES_REPLICA_SECRET_ARN_SSM_NAME, PreTokenGenerationLambda, REST_API_BASE_URL_SSM_NAME, RootGraphqlApi, RootHostedZone, RootHttpApi, RootWildcardCertificate, STATIC_HOSTING_SERVICE_TYPE, StaticHosting, buildFhirCurrentResourceChangeDetail, getDynamoDbDataStoreTableName, getPostgresReplicaSchemaName };
1346
+ export { ChildHostedZone, CognitoFixtureSeederClient, CognitoUserPool, CognitoUserPoolClient, CognitoUserPoolDomain, CognitoUserPoolKmsKey, DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES, DATA_STORE_CHANGE_DETAIL_TYPE, DATA_STORE_CHANGE_EVENT_SOURCE, DataEventBus, DataStoreHistoricalArchive, DataStorePostgresReplica, DiscoverableStringParameter, DynamoDbDataStore, OpenHiApp, OpenHiAuthService, OpenHiDataService, OpenHiEnvironment, OpenHiGlobalService, OpenHiGraphqlService, OpenHiRestApiService, OpenHiService, OpenHiStage, OpsEventBus, POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME, POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME, POSTGRES_REPLICA_SECRET_ARN_SSM_NAME, PostAuthenticationLambda, PreTokenGenerationLambda, REST_API_BASE_URL_SSM_NAME, RootGraphqlApi, RootHostedZone, RootHttpApi, RootWildcardCertificate, STATIC_HOSTING_SERVICE_TYPE, StaticHosting, buildFhirCurrentResourceChangeDetail, getDynamoDbDataStoreTableName, getPostgresReplicaSchemaName };
1318
1347
  export type { BuildParameterNameProps, ChildHostedZoneProps, CognitoFixtureSeederClientProps, DataStoreHistoricalArchiveProps, DataStorePostgresReplicaProps, DiscoverableStringParameterProps, DynamoDbDataStoreProps, FhirCurrentResourceChangeDetail, OpenHiAppProps, OpenHiAuthServiceProps, OpenHiDataServiceProps, OpenHiEnvironmentProps, OpenHiGlobalServiceProps, OpenHiGraphqlServiceProps, OpenHiRestApiServiceProps, OpenHiServiceProps, OpenHiServiceType, OpenHiStageProps, RootGraphqlApiProps, RootHttpApiProps, StaticHostingProps };
package/lib/index.js CHANGED
@@ -120,6 +120,7 @@ __export(src_exports, {
120
120
  POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME: () => POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,
121
121
  POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME: () => POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,
122
122
  POSTGRES_REPLICA_SECRET_ARN_SSM_NAME: () => POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,
123
+ PostAuthenticationLambda: () => PostAuthenticationLambda,
123
124
  PreTokenGenerationLambda: () => PreTokenGenerationLambda,
124
125
  REST_API_BASE_URL_SSM_NAME: () => REST_API_BASE_URL_SSM_NAME,
125
126
  RootGraphqlApi: () => RootGraphqlApi,
@@ -722,13 +723,13 @@ var CognitoUserPoolKmsKey = class extends import_aws_kms.Key {
722
723
  */
723
724
  CognitoUserPoolKmsKey.SSM_PARAM_NAME = "COGNITO_USER_POOL_KMS_KEY";
724
725
 
725
- // src/components/cognito/pre-token-generation-lambda.ts
726
+ // src/components/cognito/post-authentication-lambda.ts
726
727
  var import_node_fs = __toESM(require("fs"));
727
728
  var import_node_path = __toESM(require("path"));
728
729
  var import_aws_lambda = require("aws-cdk-lib/aws-lambda");
729
730
  var import_aws_lambda_nodejs = require("aws-cdk-lib/aws-lambda-nodejs");
730
731
  var import_constructs = require("constructs");
731
- var HANDLER_NAME = "pre-token-generation.handler.js";
732
+ var HANDLER_NAME = "post-authentication.handler.js";
732
733
  function resolveHandlerEntry(dirname) {
733
734
  const sameDir = import_node_path.default.join(dirname, HANDLER_NAME);
734
735
  if (import_node_fs.default.existsSync(sameDir)) {
@@ -737,9 +738,9 @@ function resolveHandlerEntry(dirname) {
737
738
  const fromLib = import_node_path.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME);
738
739
  return fromLib;
739
740
  }
740
- var PreTokenGenerationLambda = class extends import_constructs.Construct {
741
+ var PostAuthenticationLambda = class extends import_constructs.Construct {
741
742
  constructor(scope) {
742
- super(scope, "pre-token-generation-lambda");
743
+ super(scope, "post-authentication-lambda");
743
744
  this.lambda = new import_aws_lambda_nodejs.NodejsFunction(this, "handler", {
744
745
  entry: resolveHandlerEntry(__dirname),
745
746
  runtime: import_aws_lambda.Runtime.NODEJS_LATEST,
@@ -748,6 +749,32 @@ var PreTokenGenerationLambda = class extends import_constructs.Construct {
748
749
  }
749
750
  };
750
751
 
752
+ // src/components/cognito/pre-token-generation-lambda.ts
753
+ var import_node_fs2 = __toESM(require("fs"));
754
+ var import_node_path2 = __toESM(require("path"));
755
+ var import_aws_lambda2 = require("aws-cdk-lib/aws-lambda");
756
+ var import_aws_lambda_nodejs2 = require("aws-cdk-lib/aws-lambda-nodejs");
757
+ var import_constructs2 = require("constructs");
758
+ var HANDLER_NAME2 = "pre-token-generation.handler.js";
759
+ function resolveHandlerEntry2(dirname) {
760
+ const sameDir = import_node_path2.default.join(dirname, HANDLER_NAME2);
761
+ if (import_node_fs2.default.existsSync(sameDir)) {
762
+ return sameDir;
763
+ }
764
+ const fromLib = import_node_path2.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME2);
765
+ return fromLib;
766
+ }
767
+ var PreTokenGenerationLambda = class extends import_constructs2.Construct {
768
+ constructor(scope) {
769
+ super(scope, "pre-token-generation-lambda");
770
+ this.lambda = new import_aws_lambda_nodejs2.NodejsFunction(this, "handler", {
771
+ entry: resolveHandlerEntry2(__dirname),
772
+ runtime: import_aws_lambda2.Runtime.NODEJS_LATEST,
773
+ memorySize: 1024
774
+ });
775
+ }
776
+ };
777
+
751
778
  // src/components/dynamodb/dynamodb-stream-record.ts
752
779
  function dynamodbValueToJs(av) {
753
780
  if (av.S !== void 0) {
@@ -870,23 +897,23 @@ function buildFhirCurrentResourceChangeDetail(record, keys) {
870
897
  }
871
898
 
872
899
  // src/components/dynamodb/data-store-historical-archive.ts
873
- var import_node_fs2 = __toESM(require("fs"));
874
- var import_node_path2 = __toESM(require("path"));
900
+ var import_node_fs3 = __toESM(require("fs"));
901
+ var import_node_path3 = __toESM(require("path"));
875
902
  var import_aws_cdk_lib7 = require("aws-cdk-lib");
876
903
  var kinesisfirehose = __toESM(require("aws-cdk-lib/aws-kinesisfirehose"));
877
- var import_aws_lambda2 = require("aws-cdk-lib/aws-lambda");
878
- var import_aws_lambda_nodejs2 = require("aws-cdk-lib/aws-lambda-nodejs");
904
+ var import_aws_lambda3 = require("aws-cdk-lib/aws-lambda");
905
+ var import_aws_lambda_nodejs3 = require("aws-cdk-lib/aws-lambda-nodejs");
879
906
  var s3 = __toESM(require("aws-cdk-lib/aws-s3"));
880
- var import_constructs2 = require("constructs");
881
- var HANDLER_NAME2 = "firehose-archive-transform.handler.js";
882
- function resolveHandlerEntry2(dirname) {
883
- const sameDir = import_node_path2.default.join(dirname, HANDLER_NAME2);
884
- if (import_node_fs2.default.existsSync(sameDir)) {
907
+ var import_constructs3 = require("constructs");
908
+ var HANDLER_NAME3 = "firehose-archive-transform.handler.js";
909
+ function resolveHandlerEntry3(dirname) {
910
+ const sameDir = import_node_path3.default.join(dirname, HANDLER_NAME3);
911
+ if (import_node_fs3.default.existsSync(sameDir)) {
885
912
  return sameDir;
886
913
  }
887
- return import_node_path2.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME2);
914
+ return import_node_path3.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME3);
888
915
  }
889
- var DataStoreHistoricalArchive = class extends import_constructs2.Construct {
916
+ var DataStoreHistoricalArchive = class extends import_constructs3.Construct {
890
917
  constructor(scope, id, props) {
891
918
  super(scope, id);
892
919
  this.archiveBucket = new s3.Bucket(this, "ArchiveBucket", {
@@ -906,9 +933,9 @@ var DataStoreHistoricalArchive = class extends import_constructs2.Construct {
906
933
  versioned: false
907
934
  }) : void 0;
908
935
  this.putEventsFailureDlqBucket = putEventsFailureDlqBucket;
909
- this.transformFunction = new import_aws_lambda_nodejs2.NodejsFunction(this, "FirehoseTransform", {
910
- entry: resolveHandlerEntry2(__dirname),
911
- runtime: import_aws_lambda2.Runtime.NODEJS_LATEST,
936
+ this.transformFunction = new import_aws_lambda_nodejs3.NodejsFunction(this, "FirehoseTransform", {
937
+ entry: resolveHandlerEntry3(__dirname),
938
+ runtime: import_aws_lambda3.Runtime.NODEJS_LATEST,
912
939
  memorySize: 512,
913
940
  timeout: import_aws_cdk_lib7.Duration.minutes(1),
914
941
  description: "Firehose transform: filter CURRENT resource rows, S3 keys, EventBridge PutEvents",
@@ -1074,27 +1101,27 @@ var OpsEventBus = class _OpsEventBus extends import_aws_events2.EventBus {
1074
1101
  };
1075
1102
 
1076
1103
  // src/components/postgres/data-store-postgres-replica.ts
1077
- var import_node_fs3 = __toESM(require("fs"));
1078
- var import_node_path3 = __toESM(require("path"));
1104
+ var import_node_fs4 = __toESM(require("fs"));
1105
+ var import_node_path4 = __toESM(require("path"));
1079
1106
  var import_aws_cdk_lib8 = require("aws-cdk-lib");
1080
1107
  var ec2 = __toESM(require("aws-cdk-lib/aws-ec2"));
1081
- var import_aws_lambda3 = require("aws-cdk-lib/aws-lambda");
1108
+ var import_aws_lambda4 = require("aws-cdk-lib/aws-lambda");
1082
1109
  var import_aws_lambda_event_sources = require("aws-cdk-lib/aws-lambda-event-sources");
1083
- var import_aws_lambda_nodejs3 = require("aws-cdk-lib/aws-lambda-nodejs");
1110
+ var import_aws_lambda_nodejs4 = require("aws-cdk-lib/aws-lambda-nodejs");
1084
1111
  var rds = __toESM(require("aws-cdk-lib/aws-rds"));
1085
- var import_constructs3 = require("constructs");
1086
- var HANDLER_NAME3 = "data-store-postgres-replication.handler.js";
1112
+ var import_constructs4 = require("constructs");
1113
+ var HANDLER_NAME4 = "data-store-postgres-replication.handler.js";
1087
1114
  var DEFAULT_DATABASE_NAME = "openhi";
1088
1115
  var SCHEMA_NAME_PATTERN = /^[a-z_][a-z0-9_]{0,62}$/;
1089
1116
  var POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME = "POSTGRES_REPLICA_CLUSTER_ARN";
1090
1117
  var POSTGRES_REPLICA_SECRET_ARN_SSM_NAME = "POSTGRES_REPLICA_SECRET_ARN";
1091
1118
  var POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME = "POSTGRES_REPLICA_DATABASE_NAME";
1092
- function resolveHandlerEntry3(dirname) {
1093
- const sameDir = import_node_path3.default.join(dirname, HANDLER_NAME3);
1094
- if (import_node_fs3.default.existsSync(sameDir)) {
1119
+ function resolveHandlerEntry4(dirname) {
1120
+ const sameDir = import_node_path4.default.join(dirname, HANDLER_NAME4);
1121
+ if (import_node_fs4.default.existsSync(sameDir)) {
1095
1122
  return sameDir;
1096
1123
  }
1097
- return import_node_path3.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME3);
1124
+ return import_node_path4.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME4);
1098
1125
  }
1099
1126
  function getPostgresReplicaSchemaName(branchHash) {
1100
1127
  const candidate = `b_${branchHash.toLowerCase()}`;
@@ -1105,7 +1132,7 @@ function getPostgresReplicaSchemaName(branchHash) {
1105
1132
  }
1106
1133
  return candidate;
1107
1134
  }
1108
- var DataStorePostgresReplica = class extends import_constructs3.Construct {
1135
+ var DataStorePostgresReplica = class extends import_constructs4.Construct {
1109
1136
  /**
1110
1137
  * Resolve the cluster ARN published by an upstream {@link DataStorePostgresReplica}.
1111
1138
  * Use from any stack that needs to grant `rds-data:ExecuteStatement` against
@@ -1174,9 +1201,9 @@ var DataStorePostgresReplica = class extends import_constructs3.Construct {
1174
1201
  enableDataApi: true
1175
1202
  });
1176
1203
  this.publishCoordinatesToSsm();
1177
- this.replicationFunction = new import_aws_lambda_nodejs3.NodejsFunction(this, "ReplicationFunction", {
1178
- entry: resolveHandlerEntry3(__dirname),
1179
- runtime: import_aws_lambda3.Runtime.NODEJS_LATEST,
1204
+ this.replicationFunction = new import_aws_lambda_nodejs4.NodejsFunction(this, "ReplicationFunction", {
1205
+ entry: resolveHandlerEntry4(__dirname),
1206
+ runtime: import_aws_lambda4.Runtime.NODEJS_LATEST,
1180
1207
  memorySize: 512,
1181
1208
  timeout: import_aws_cdk_lib8.Duration.minutes(1),
1182
1209
  vpc: this.vpc,
@@ -1203,7 +1230,7 @@ var DataStorePostgresReplica = class extends import_constructs3.Construct {
1203
1230
  this.cluster.connections.allowDefaultPortFrom(this.replicationFunction);
1204
1231
  this.replicationFunction.addEventSource(
1205
1232
  new import_aws_lambda_event_sources.KinesisEventSource(props.kinesisStream, {
1206
- startingPosition: import_aws_lambda3.StartingPosition.LATEST,
1233
+ startingPosition: import_aws_lambda4.StartingPosition.LATEST,
1207
1234
  batchSize: 100,
1208
1235
  maxBatchingWindow: import_aws_cdk_lib8.Duration.seconds(5),
1209
1236
  retryAttempts: 10,
@@ -1257,8 +1284,8 @@ var ChildHostedZone = class extends import_aws_route53.HostedZone {
1257
1284
  ChildHostedZone.SSM_PARAM_NAME = "CHILDHOSTEDZONE";
1258
1285
 
1259
1286
  // src/components/route-53/root-hosted-zone.ts
1260
- var import_constructs4 = require("constructs");
1261
- var RootHostedZone = class extends import_constructs4.Construct {
1287
+ var import_constructs5 = require("constructs");
1288
+ var RootHostedZone = class extends import_constructs5.Construct {
1262
1289
  };
1263
1290
 
1264
1291
  // src/components/static-hosting/static-hosting.ts
@@ -1266,9 +1293,9 @@ var import_aws_cloudfront = require("aws-cdk-lib/aws-cloudfront");
1266
1293
  var import_aws_cloudfront_origins = require("aws-cdk-lib/aws-cloudfront-origins");
1267
1294
  var import_aws_s3 = require("aws-cdk-lib/aws-s3");
1268
1295
  var import_core = require("aws-cdk-lib/core");
1269
- var import_constructs5 = require("constructs");
1296
+ var import_constructs6 = require("constructs");
1270
1297
  var STATIC_HOSTING_SERVICE_TYPE = "website";
1271
- var _StaticHosting = class _StaticHosting extends import_constructs5.Construct {
1298
+ var _StaticHosting = class _StaticHosting extends import_constructs6.Construct {
1272
1299
  constructor(scope, id, props = {}) {
1273
1300
  super(scope, id);
1274
1301
  const stack = OpenHiService.of(scope);
@@ -1322,7 +1349,9 @@ var StaticHosting = _StaticHosting;
1322
1349
  // src/services/open-hi-auth-service.ts
1323
1350
  var import_config4 = __toESM(require_lib());
1324
1351
  var import_aws_cognito5 = require("aws-cdk-lib/aws-cognito");
1352
+ var import_aws_iam = require("aws-cdk-lib/aws-iam");
1325
1353
  var import_aws_kms2 = require("aws-cdk-lib/aws-kms");
1354
+ var import_core2 = require("aws-cdk-lib/core");
1326
1355
  var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
1327
1356
  /**
1328
1357
  * Returns an IUserPool by looking up the Auth stack's User Pool ID from SSM.
@@ -1400,7 +1429,9 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
1400
1429
  this.props = props;
1401
1430
  this.userPoolKmsKey = this.createUserPoolKmsKey();
1402
1431
  this.preTokenGenerationLambda = this.createPreTokenGenerationLambda();
1432
+ this.postAuthenticationLambda = this.createPostAuthenticationLambda();
1403
1433
  this.userPool = this.createUserPool();
1434
+ this.grantPostAuthenticationPermissions();
1404
1435
  this.userPoolClient = this.createUserPoolClient();
1405
1436
  this.userPoolDomain = this.createUserPoolDomain();
1406
1437
  this.fixtureSeederClient = this.createFixtureSeederClient();
@@ -1427,6 +1458,15 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
1427
1458
  const construct = new PreTokenGenerationLambda(this);
1428
1459
  return construct.lambda;
1429
1460
  }
1461
+ /**
1462
+ * Creates the Post Authentication Lambda (Cognito trigger). Calls
1463
+ * AdminUserGlobalSignOut on every sign-in to enforce single-device-per-user
1464
+ * sessions per ADR 2026-03-17-01.
1465
+ */
1466
+ createPostAuthenticationLambda() {
1467
+ const construct = new PostAuthenticationLambda(this);
1468
+ return construct.lambda;
1469
+ }
1430
1470
  /**
1431
1471
  * Creates the Cognito User Pool and exports its ID to SSM.
1432
1472
  * Look up via {@link OpenHiAuthService.userPoolFromConstruct}.
@@ -1442,6 +1482,10 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
1442
1482
  this.preTokenGenerationLambda,
1443
1483
  import_aws_cognito5.LambdaVersion.V2_0
1444
1484
  );
1485
+ userPool.addTrigger(
1486
+ import_aws_cognito5.UserPoolOperation.POST_AUTHENTICATION,
1487
+ this.postAuthenticationLambda
1488
+ );
1445
1489
  new DiscoverableStringParameter(this, "user-pool-param", {
1446
1490
  ssmParamName: CognitoUserPool.SSM_PARAM_NAME,
1447
1491
  stringValue: userPool.userPoolId,
@@ -1449,6 +1493,33 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
1449
1493
  });
1450
1494
  return userPool;
1451
1495
  }
1496
+ /**
1497
+ * Grants the Post Authentication Lambda permission to call
1498
+ * `cognito-idp:AdminUserGlobalSignOut`.
1499
+ *
1500
+ * Scoped via `Stack.of(this).formatArn` rather than `userPool.userPoolArn`
1501
+ * because the User Pool registers this Lambda as a Post Authentication
1502
+ * trigger, creating the cycle:
1503
+ * userPool → lambda (trigger ARN) → role policy → userPool ARN.
1504
+ * Using `formatArn` avoids referencing the User Pool resource directly
1505
+ * while still scoping to user pools in this account+region. The Lambda
1506
+ * is invoked only by Cognito with a Cognito-provided `event.userPoolId`,
1507
+ * so the runtime target is constrained by the trigger contract.
1508
+ */
1509
+ grantPostAuthenticationPermissions() {
1510
+ this.postAuthenticationLambda.addToRolePolicy(
1511
+ new import_aws_iam.PolicyStatement({
1512
+ actions: ["cognito-idp:AdminUserGlobalSignOut"],
1513
+ resources: [
1514
+ import_core2.Stack.of(this).formatArn({
1515
+ service: "cognito-idp",
1516
+ resource: "userpool",
1517
+ resourceName: "*"
1518
+ })
1519
+ ]
1520
+ })
1521
+ );
1522
+ }
1452
1523
  /**
1453
1524
  * Creates the User Pool Client and exports its ID to SSM (AUTH service type).
1454
1525
  * Look up via {@link OpenHiAuthService.userPoolClientFromConstruct}.
@@ -1620,10 +1691,10 @@ var import_config5 = __toESM(require_lib());
1620
1691
  var import_aws_apigatewayv22 = require("aws-cdk-lib/aws-apigatewayv2");
1621
1692
  var import_aws_apigatewayv2_authorizers = require("aws-cdk-lib/aws-apigatewayv2-authorizers");
1622
1693
  var import_aws_apigatewayv2_integrations = require("aws-cdk-lib/aws-apigatewayv2-integrations");
1623
- var import_aws_iam = require("aws-cdk-lib/aws-iam");
1694
+ var import_aws_iam2 = require("aws-cdk-lib/aws-iam");
1624
1695
  var import_aws_route533 = require("aws-cdk-lib/aws-route53");
1625
1696
  var import_aws_route53_targets = require("aws-cdk-lib/aws-route53-targets");
1626
- var import_core2 = require("aws-cdk-lib/core");
1697
+ var import_core3 = require("aws-cdk-lib/core");
1627
1698
 
1628
1699
  // src/services/open-hi-data-service.ts
1629
1700
  var import_aws_dynamodb2 = require("aws-cdk-lib/aws-dynamodb");
@@ -1727,52 +1798,52 @@ _OpenHiDataService.SERVICE_TYPE = "data";
1727
1798
  var OpenHiDataService = _OpenHiDataService;
1728
1799
 
1729
1800
  // src/data/lambda/cors-options-lambda.ts
1730
- var import_node_fs4 = __toESM(require("fs"));
1731
- var import_node_path4 = __toESM(require("path"));
1732
- var import_aws_lambda4 = require("aws-cdk-lib/aws-lambda");
1733
- var import_aws_lambda_nodejs4 = require("aws-cdk-lib/aws-lambda-nodejs");
1734
- var import_constructs6 = require("constructs");
1735
- var HANDLER_NAME4 = "cors-options-lambda.handler.js";
1736
- function resolveHandlerEntry4(dirname) {
1737
- const sameDir = import_node_path4.default.join(dirname, HANDLER_NAME4);
1738
- if (import_node_fs4.default.existsSync(sameDir)) {
1801
+ var import_node_fs5 = __toESM(require("fs"));
1802
+ var import_node_path5 = __toESM(require("path"));
1803
+ var import_aws_lambda5 = require("aws-cdk-lib/aws-lambda");
1804
+ var import_aws_lambda_nodejs5 = require("aws-cdk-lib/aws-lambda-nodejs");
1805
+ var import_constructs7 = require("constructs");
1806
+ var HANDLER_NAME5 = "cors-options-lambda.handler.js";
1807
+ function resolveHandlerEntry5(dirname) {
1808
+ const sameDir = import_node_path5.default.join(dirname, HANDLER_NAME5);
1809
+ if (import_node_fs5.default.existsSync(sameDir)) {
1739
1810
  return sameDir;
1740
1811
  }
1741
- const fromLib = import_node_path4.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME4);
1812
+ const fromLib = import_node_path5.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME5);
1742
1813
  return fromLib;
1743
1814
  }
1744
- var CorsOptionsLambda = class extends import_constructs6.Construct {
1815
+ var CorsOptionsLambda = class extends import_constructs7.Construct {
1745
1816
  constructor(scope, id = "cors-options-lambda") {
1746
1817
  super(scope, id);
1747
- this.lambda = new import_aws_lambda_nodejs4.NodejsFunction(this, "handler", {
1748
- entry: resolveHandlerEntry4(__dirname),
1749
- runtime: import_aws_lambda4.Runtime.NODEJS_LATEST,
1818
+ this.lambda = new import_aws_lambda_nodejs5.NodejsFunction(this, "handler", {
1819
+ entry: resolveHandlerEntry5(__dirname),
1820
+ runtime: import_aws_lambda5.Runtime.NODEJS_LATEST,
1750
1821
  memorySize: 128
1751
1822
  });
1752
1823
  }
1753
1824
  };
1754
1825
 
1755
1826
  // src/data/lambda/rest-api-lambda.ts
1756
- var import_node_fs5 = __toESM(require("fs"));
1757
- var import_node_path5 = __toESM(require("path"));
1758
- var import_aws_lambda5 = require("aws-cdk-lib/aws-lambda");
1759
- var import_aws_lambda_nodejs5 = require("aws-cdk-lib/aws-lambda-nodejs");
1760
- var import_constructs7 = require("constructs");
1761
- var HANDLER_NAME5 = "rest-api-lambda.handler.js";
1762
- function resolveHandlerEntry5(dirname) {
1763
- const sameDir = import_node_path5.default.join(dirname, HANDLER_NAME5);
1764
- if (import_node_fs5.default.existsSync(sameDir)) {
1827
+ var import_node_fs6 = __toESM(require("fs"));
1828
+ var import_node_path6 = __toESM(require("path"));
1829
+ var import_aws_lambda6 = require("aws-cdk-lib/aws-lambda");
1830
+ var import_aws_lambda_nodejs6 = require("aws-cdk-lib/aws-lambda-nodejs");
1831
+ var import_constructs8 = require("constructs");
1832
+ var HANDLER_NAME6 = "rest-api-lambda.handler.js";
1833
+ function resolveHandlerEntry6(dirname) {
1834
+ const sameDir = import_node_path6.default.join(dirname, HANDLER_NAME6);
1835
+ if (import_node_fs6.default.existsSync(sameDir)) {
1765
1836
  return sameDir;
1766
1837
  }
1767
- const fromLib = import_node_path5.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME5);
1838
+ const fromLib = import_node_path6.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME6);
1768
1839
  return fromLib;
1769
1840
  }
1770
- var RestApiLambda = class extends import_constructs7.Construct {
1841
+ var RestApiLambda = class extends import_constructs8.Construct {
1771
1842
  constructor(scope, props) {
1772
1843
  super(scope, "rest-api-lambda");
1773
- this.lambda = new import_aws_lambda_nodejs5.NodejsFunction(this, "handler", {
1774
- entry: resolveHandlerEntry5(__dirname),
1775
- runtime: import_aws_lambda5.Runtime.NODEJS_LATEST,
1844
+ this.lambda = new import_aws_lambda_nodejs6.NodejsFunction(this, "handler", {
1845
+ entry: resolveHandlerEntry6(__dirname),
1846
+ runtime: import_aws_lambda6.Runtime.NODEJS_LATEST,
1776
1847
  memorySize: 1024,
1777
1848
  environment: {
1778
1849
  DYNAMO_TABLE_NAME: props.dynamoTableName,
@@ -1914,8 +1985,8 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
1914
1985
  postgresSchema
1915
1986
  });
1916
1987
  lambda.addToRolePolicy(
1917
- new import_aws_iam.PolicyStatement({
1918
- effect: import_aws_iam.Effect.ALLOW,
1988
+ new import_aws_iam2.PolicyStatement({
1989
+ effect: import_aws_iam2.Effect.ALLOW,
1919
1990
  actions: [
1920
1991
  "rds-data:ExecuteStatement",
1921
1992
  "rds-data:BatchExecuteStatement"
@@ -1924,8 +1995,8 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
1924
1995
  })
1925
1996
  );
1926
1997
  lambda.addToRolePolicy(
1927
- new import_aws_iam.PolicyStatement({
1928
- effect: import_aws_iam.Effect.ALLOW,
1998
+ new import_aws_iam2.PolicyStatement({
1999
+ effect: import_aws_iam2.Effect.ALLOW,
1929
2000
  actions: ["secretsmanager:GetSecretValue"],
1930
2001
  resources: [postgresSecretArn]
1931
2002
  })
@@ -1943,15 +2014,15 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
1943
2014
  ];
1944
2015
  dataStoreTable.grant(lambda, ...dynamoActions);
1945
2016
  lambda.addToRolePolicy(
1946
- new import_aws_iam.PolicyStatement({
1947
- effect: import_aws_iam.Effect.ALLOW,
2017
+ new import_aws_iam2.PolicyStatement({
2018
+ effect: import_aws_iam2.Effect.ALLOW,
1948
2019
  actions: [...dynamoActions],
1949
2020
  resources: [`${dataStoreTable.tableArn}/index/*`]
1950
2021
  })
1951
2022
  );
1952
2023
  lambda.addToRolePolicy(
1953
- new import_aws_iam.PolicyStatement({
1954
- effect: import_aws_iam.Effect.ALLOW,
2024
+ new import_aws_iam2.PolicyStatement({
2025
+ effect: import_aws_iam2.Effect.ALLOW,
1955
2026
  actions: [
1956
2027
  "ssm:GetParameter",
1957
2028
  "ssm:GetParameters",
@@ -2037,7 +2108,7 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
2037
2108
  "Authorization"
2038
2109
  ],
2039
2110
  allowCredentials: cors.allowCredentials ?? true,
2040
- maxAge: cors.maxAge ?? import_core2.Duration.days(1),
2111
+ maxAge: cors.maxAge ?? import_core3.Duration.days(1),
2041
2112
  ...cors.exposeHeaders !== void 0 && {
2042
2113
  exposeHeaders: cors.exposeHeaders
2043
2114
  }
@@ -2127,6 +2198,7 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
2127
2198
  POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,
2128
2199
  POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,
2129
2200
  POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,
2201
+ PostAuthenticationLambda,
2130
2202
  PreTokenGenerationLambda,
2131
2203
  REST_API_BASE_URL_SSM_NAME,
2132
2204
  RootGraphqlApi,