@aws/nx-plugin 0.62.4 → 0.63.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aws/nx-plugin",
3
- "version": "0.62.4",
3
+ "version": "0.63.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/awslabs/nx-plugin-for-aws.git",
@@ -7,11 +7,11 @@ exports[`fastapi project generator > should match snapshot > main-snapshot 1`] =
7
7
  "apps/test_api/proj_test_api/init.py": "import os
8
8
  import uuid
9
9
  from collections.abc import Callable
10
+ from urllib.parse import urlparse
10
11
 
11
12
  from aws_lambda_powertools import Logger, Metrics, Tracer
12
13
  from aws_lambda_powertools.metrics import MetricUnit
13
14
  from fastapi import FastAPI, Request, Response
14
- from fastapi.middleware.cors import CORSMiddleware
15
15
  from fastapi.openapi.utils import get_openapi
16
16
  from fastapi.responses import JSONResponse
17
17
  from fastapi.routing import APIRoute
@@ -46,10 +46,25 @@ lambda_handler = logger.inject_lambda_context(lambda_handler, clear_state=True)
46
46
  lambda_handler = metrics.log_metrics(lambda_handler, capture_cold_start_metric=True)
47
47
 
48
48
  # Add cors middleware
49
- app.add_middleware(CORSMiddleware,
50
- allow_origins=['*'],
51
- allow_methods=['*'],
52
- allow_headers=['*'])
49
+ @app.middleware("http")
50
+ async def cors_middleware(request: Request, call_next):
51
+ response = await call_next(request)
52
+
53
+ origin = request.headers.get("origin")
54
+ allowed_origins = os.environ.get('ALLOWED_ORIGINS', '').split(',') if os.environ.get('ALLOWED_ORIGINS') else []
55
+
56
+ is_localhost = origin and urlparse(origin).hostname in ['localhost', '127.0.0.1']
57
+ is_allowed_origin = origin and origin in allowed_origins
58
+
59
+ cors_origin = '*'
60
+ if allowed_origins and not is_localhost:
61
+ cors_origin = origin if is_allowed_origin else allowed_origins[0]
62
+
63
+ response.headers["Access-Control-Allow-Origin"] = cors_origin
64
+ response.headers["Access-Control-Allow-Methods"] = "*"
65
+ response.headers["Access-Control-Allow-Headers"] = "*"
66
+
67
+ return response
53
68
 
54
69
  # Add exception middleware(s)
55
70
  app.add_middleware(ExceptionMiddleware, handlers=app.exception_handlers)
@@ -311,6 +326,7 @@ export class HttpApi<
311
326
  exports[`fastapi project generator > should set up shared constructs for http > test-api.ts 1`] = `
312
327
  "import { Construct } from 'constructs';
313
328
  import * as url from 'url';
329
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
314
330
  import {
315
331
  Code,
316
332
  Runtime,
@@ -319,7 +335,7 @@ import {
319
335
  Tracing,
320
336
  } from 'aws-cdk-lib/aws-lambda';
321
337
  import { Duration } from 'aws-cdk-lib';
322
- import { CorsHttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
338
+ import { CorsHttpMethod, CfnApi } from 'aws-cdk-lib/aws-apigatewayv2';
323
339
  import { HttpIamAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
324
340
  import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
325
341
  import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
@@ -419,6 +435,48 @@ export class TestApi<
419
435
  });
420
436
  }
421
437
 
438
+ /**
439
+ * Restricts CORS to the website CloudFront distribution domains
440
+ *
441
+ * Configures the CloudFront distribution domains as the only permitted CORS origins
442
+ * (other than local host with default ports) in the API gateway
443
+ * The CORS origins are not configured within the AWS Lambda integrations since
444
+ * the associated header is controlled by API Gateway v2
445
+ *
446
+ * @param cloudFrontDistribution - The CloudFront distribution to grant CORS from
447
+ */
448
+ public restrictCorsTo(
449
+ ...websites: { cloudFrontDistribution: Distribution }[]
450
+ ) {
451
+ const allowedOrigins = websites.map(
452
+ ({ cloudFrontDistribution }) =>
453
+ \`https://\${cloudFrontDistribution.distributionDomainName}\`,
454
+ );
455
+
456
+ const cfnApi = this.api.node.defaultChild;
457
+ if (!(cfnApi instanceof CfnApi)) {
458
+ throw new Error(
459
+ 'Unable to configure CORS: API default child is not a CfnApi instance',
460
+ );
461
+ }
462
+
463
+ cfnApi.corsConfiguration = {
464
+ allowOrigins: [
465
+ 'http://localhost:4200',
466
+ 'http://localhost:4300',
467
+ ...allowedOrigins,
468
+ ],
469
+ allowMethods: [CorsHttpMethod.ANY],
470
+ allowHeaders: [
471
+ 'authorization',
472
+ 'content-type',
473
+ 'x-amz-content-sha256',
474
+ 'x-amz-date',
475
+ 'x-amz-security-token',
476
+ ],
477
+ };
478
+ }
479
+
422
480
  /**
423
481
  * Grants IAM permissions to invoke any method on this API.
424
482
  *
@@ -801,6 +859,7 @@ export class RestApi<
801
859
  exports[`fastapi project generator > should set up shared constructs for rest > test-api.ts 1`] = `
802
860
  "import { Construct } from 'constructs';
803
861
  import * as url from 'url';
862
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
804
863
  import {
805
864
  Code,
806
865
  Runtime,
@@ -921,6 +980,34 @@ export class TestApi<
921
980
  });
922
981
  }
923
982
 
983
+ /**
984
+ * Restricts CORS to the website CloudFront distribution domains
985
+ *
986
+ * Configures the CloudFront distribution domains as the only permitted CORS origins
987
+ * (other than local host) in the AWS Lambda integrations
988
+ *
989
+ * Note that this restriction is not applied to preflight OPTIONS
990
+ *
991
+ * @param websites - The CloudFront distribution to grant CORS from
992
+ */
993
+ public restrictCorsTo(
994
+ ...websites: { cloudFrontDistribution: Distribution }[]
995
+ ) {
996
+ const allowedOrigins = websites
997
+ .map(
998
+ ({ cloudFrontDistribution }) =>
999
+ \`https://\${cloudFrontDistribution.distributionDomainName}\`,
1000
+ )
1001
+ .join(',');
1002
+
1003
+ // Set ALLOWED_ORIGINS environment variable for all Lambda integrations
1004
+ Object.values(this.integrations).forEach((integration) => {
1005
+ if ('handler' in integration && integration.handler instanceof Function) {
1006
+ integration.handler.addEnvironment('ALLOWED_ORIGINS', allowedOrigins);
1007
+ }
1008
+ });
1009
+ }
1010
+
924
1011
  /**
925
1012
  * Grants IAM permissions to invoke any method on this API.
926
1013
  *
@@ -1,11 +1,11 @@
1
1
  import os
2
2
  import uuid
3
3
  from collections.abc import Callable
4
+ from urllib.parse import urlparse
4
5
 
5
6
  from aws_lambda_powertools import Logger, Metrics, Tracer
6
7
  from aws_lambda_powertools.metrics import MetricUnit
7
8
  from fastapi import FastAPI, Request, Response
8
- from fastapi.middleware.cors import CORSMiddleware
9
9
  from fastapi.openapi.utils import get_openapi
10
10
  from fastapi.responses import JSONResponse
11
11
  from fastapi.routing import APIRoute
@@ -40,10 +40,25 @@ lambda_handler = logger.inject_lambda_context(lambda_handler, clear_state=True)
40
40
  lambda_handler = metrics.log_metrics(lambda_handler, capture_cold_start_metric=True)
41
41
 
42
42
  # Add cors middleware
43
- app.add_middleware(CORSMiddleware,
44
- allow_origins=['*'],
45
- allow_methods=['*'],
46
- allow_headers=['*'])
43
+ @app.middleware("http")
44
+ async def cors_middleware(request: Request, call_next):
45
+ response = await call_next(request)
46
+
47
+ origin = request.headers.get("origin")
48
+ allowed_origins = os.environ.get('ALLOWED_ORIGINS', '').split(',') if os.environ.get('ALLOWED_ORIGINS') else []
49
+
50
+ is_localhost = origin and urlparse(origin).hostname in ['localhost', '127.0.0.1']
51
+ is_allowed_origin = origin and origin in allowed_origins
52
+
53
+ cors_origin = '*'
54
+ if allowed_origins and not is_localhost:
55
+ cors_origin = origin if is_allowed_origin else allowed_origins[0]
56
+
57
+ response.headers["Access-Control-Allow-Origin"] = cors_origin
58
+ response.headers["Access-Control-Allow-Methods"] = "*"
59
+ response.headers["Access-Control-Allow-Headers"] = "*"
60
+
61
+ return response
47
62
 
48
63
  # Add exception middleware(s)
49
64
  app.add_middleware(ExceptionMiddleware, handlers=app.exception_handlers)
@@ -34,6 +34,7 @@ exports[`tsSmithyApiGenerator > should configure git and eslint ignores for gene
34
34
  exports[`tsSmithyApiGenerator > should generate smithy ts api with Cognito auth > cognito-auth-infra.ts 1`] = `
35
35
  "import { Construct } from 'constructs';
36
36
  import * as url from 'url';
37
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
37
38
  import {
38
39
  Code,
39
40
  Runtime,
@@ -162,6 +163,34 @@ export class TestApi<
162
163
  ...props,
163
164
  });
164
165
  }
166
+
167
+ /**
168
+ * Restricts CORS to the website CloudFront distribution domains
169
+ *
170
+ * Configures the CloudFront distribution domains as the only permitted CORS origins
171
+ * (other than local host) in the AWS Lambda integrations
172
+ *
173
+ * Note that this restriction is not applied to preflight OPTIONS
174
+ *
175
+ * @param websites - The CloudFront distribution to grant CORS from
176
+ */
177
+ public restrictCorsTo(
178
+ ...websites: { cloudFrontDistribution: Distribution }[]
179
+ ) {
180
+ const allowedOrigins = websites
181
+ .map(
182
+ ({ cloudFrontDistribution }) =>
183
+ \`https://\${cloudFrontDistribution.distributionDomainName}\`,
184
+ )
185
+ .join(',');
186
+
187
+ // Set ALLOWED_ORIGINS environment variable for all Lambda integrations
188
+ Object.values(this.integrations).forEach((integration) => {
189
+ if ('handler' in integration && integration.handler instanceof Function) {
190
+ integration.handler.addEnvironment('ALLOWED_ORIGINS', allowedOrigins);
191
+ }
192
+ });
193
+ }
165
194
  }
166
195
  "
167
196
  `;
@@ -169,6 +198,7 @@ export class TestApi<
169
198
  exports[`tsSmithyApiGenerator > should generate smithy ts api with IAM auth > iam-auth-infra.ts 1`] = `
170
199
  "import { Construct } from 'constructs';
171
200
  import * as url from 'url';
201
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
172
202
  import {
173
203
  Code,
174
204
  Runtime,
@@ -289,6 +319,34 @@ export class TestApi<
289
319
  });
290
320
  }
291
321
 
322
+ /**
323
+ * Restricts CORS to the website CloudFront distribution domains
324
+ *
325
+ * Configures the CloudFront distribution domains as the only permitted CORS origins
326
+ * (other than local host) in the AWS Lambda integrations
327
+ *
328
+ * Note that this restriction is not applied to preflight OPTIONS
329
+ *
330
+ * @param websites - The CloudFront distribution to grant CORS from
331
+ */
332
+ public restrictCorsTo(
333
+ ...websites: { cloudFrontDistribution: Distribution }[]
334
+ ) {
335
+ const allowedOrigins = websites
336
+ .map(
337
+ ({ cloudFrontDistribution }) =>
338
+ \`https://\${cloudFrontDistribution.distributionDomainName}\`,
339
+ )
340
+ .join(',');
341
+
342
+ // Set ALLOWED_ORIGINS environment variable for all Lambda integrations
343
+ Object.values(this.integrations).forEach((integration) => {
344
+ if ('handler' in integration && integration.handler instanceof Function) {
345
+ integration.handler.addEnvironment('ALLOWED_ORIGINS', allowedOrigins);
346
+ }
347
+ });
348
+ }
349
+
292
350
  /**
293
351
  * Grants IAM permissions to invoke any method on this API.
294
352
  *
@@ -1110,13 +1168,26 @@ export const lambdaHandler = async (
1110
1168
  return {
1111
1169
  ...apiGatewayResponse,
1112
1170
  headers: {
1113
- 'Access-Control-Allow-Origin': '*',
1171
+ 'Access-Control-Allow-Origin': getAllowedOrigin(event),
1114
1172
  'Access-Control-Allow-Methods': '*',
1115
1173
  ...apiGatewayResponse.headers,
1116
1174
  },
1117
1175
  };
1118
1176
  };
1119
1177
 
1178
+ const getAllowedOrigin = (event: APIGatewayProxyEvent) => {
1179
+ const origin = event.headers?.origin ?? event.headers?.Origin;
1180
+ const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') ?? [];
1181
+ const isLocalHost =
1182
+ origin && new Set(['localhost', '127.0.0.1']).has(new URL(origin).hostname);
1183
+ const isAllowedOrigin = origin && allowedOrigins.includes(origin);
1184
+ let corsOrigin = '*';
1185
+ if (allowedOrigins.length > 0 && !isLocalHost) {
1186
+ corsOrigin = isAllowedOrigin ? origin : allowedOrigins[0];
1187
+ }
1188
+ return corsOrigin;
1189
+ };
1190
+
1120
1191
  export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
1121
1192
  .use(captureLambdaHandler(tracer))
1122
1193
  .use(injectLambdaContext(logger))
@@ -1255,13 +1326,26 @@ export const lambdaHandler = async (
1255
1326
  return {
1256
1327
  ...apiGatewayResponse,
1257
1328
  headers: {
1258
- 'Access-Control-Allow-Origin': '*',
1329
+ 'Access-Control-Allow-Origin': getAllowedOrigin(event),
1259
1330
  'Access-Control-Allow-Methods': '*',
1260
1331
  ...apiGatewayResponse.headers,
1261
1332
  },
1262
1333
  };
1263
1334
  };
1264
1335
 
1336
+ const getAllowedOrigin = (event: APIGatewayProxyEvent) => {
1337
+ const origin = event.headers?.origin ?? event.headers?.Origin;
1338
+ const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') ?? [];
1339
+ const isLocalHost =
1340
+ origin && new Set(['localhost', '127.0.0.1']).has(new URL(origin).hostname);
1341
+ const isAllowedOrigin = origin && allowedOrigins.includes(origin);
1342
+ let corsOrigin = '*';
1343
+ if (allowedOrigins.length > 0 && !isLocalHost) {
1344
+ corsOrigin = isAllowedOrigin ? origin : allowedOrigins[0];
1345
+ }
1346
+ return corsOrigin;
1347
+ };
1348
+
1265
1349
  export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
1266
1350
  .use(captureLambdaHandler(tracer))
1267
1351
  .use(injectLambdaContext(logger))
@@ -36,13 +36,26 @@ export const lambdaHandler = async (
36
36
  return {
37
37
  ...apiGatewayResponse,
38
38
  headers: {
39
- 'Access-Control-Allow-Origin': '*',
39
+ 'Access-Control-Allow-Origin': getAllowedOrigin(event),
40
40
  'Access-Control-Allow-Methods': '*',
41
41
  ...apiGatewayResponse.headers,
42
42
  },
43
43
  };
44
44
  };
45
45
 
46
+ const getAllowedOrigin = (event: APIGatewayProxyEvent) => {
47
+ const origin = event.headers?.origin ?? event.headers?.Origin;
48
+ const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') ?? [];
49
+ const isLocalHost =
50
+ origin && new Set(['localhost', '127.0.0.1']).has(new URL(origin).hostname);
51
+ const isAllowedOrigin = origin && allowedOrigins.includes(origin);
52
+ let corsOrigin = '*';
53
+ if (allowedOrigins.length > 0 && !isLocalHost) {
54
+ corsOrigin = isAllowedOrigin ? origin : allowedOrigins[0];
55
+ }
56
+ return corsOrigin;
57
+ };
58
+
46
59
  export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
47
60
  .use(captureLambdaHandler(tracer))
48
61
  .use(injectLambdaContext(logger))
@@ -347,6 +347,7 @@ exports[`trpc backend generator > should generate with cognito auth for a REST A
347
347
  exports[`trpc backend generator > should generate with cognito auth for a REST API > packages/common/constructs/src/app/apis/test-api.ts 1`] = `
348
348
  "import { Construct } from 'constructs';
349
349
  import * as url from 'url';
350
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
350
351
  import {
351
352
  Code,
352
353
  Runtime,
@@ -476,6 +477,34 @@ export class TestApi<
476
477
  ...props,
477
478
  });
478
479
  }
480
+
481
+ /**
482
+ * Restricts CORS to the website CloudFront distribution domains
483
+ *
484
+ * Configures the CloudFront distribution domains as the only permitted CORS origins
485
+ * (other than local host) in the AWS Lambda integrations
486
+ *
487
+ * Note that this restriction is not applied to preflight OPTIONS
488
+ *
489
+ * @param websites - The CloudFront distribution to grant CORS from
490
+ */
491
+ public restrictCorsTo(
492
+ ...websites: { cloudFrontDistribution: Distribution }[]
493
+ ) {
494
+ const allowedOrigins = websites
495
+ .map(
496
+ ({ cloudFrontDistribution }) =>
497
+ \`https://\${cloudFrontDistribution.distributionDomainName}\`,
498
+ )
499
+ .join(',');
500
+
501
+ // Set ALLOWED_ORIGINS environment variable for all Lambda integrations
502
+ Object.values(this.integrations).forEach((integration) => {
503
+ if ('handler' in integration && integration.handler instanceof Function) {
504
+ integration.handler.addEnvironment('ALLOWED_ORIGINS', allowedOrigins);
505
+ }
506
+ });
507
+ }
479
508
  }
480
509
  "
481
510
  `;
@@ -511,6 +540,7 @@ exports[`trpc backend generator > should generate with cognito auth for an HTTP
511
540
  exports[`trpc backend generator > should generate with cognito auth for an HTTP API > packages/common/constructs/src/app/apis/test-api.ts 1`] = `
512
541
  "import { Construct } from 'constructs';
513
542
  import * as url from 'url';
543
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
514
544
  import {
515
545
  Code,
516
546
  Runtime,
@@ -519,7 +549,7 @@ import {
519
549
  Tracing,
520
550
  } from 'aws-cdk-lib/aws-lambda';
521
551
  import { Duration } from 'aws-cdk-lib';
522
- import { CorsHttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
552
+ import { CorsHttpMethod, CfnApi } from 'aws-cdk-lib/aws-apigatewayv2';
523
553
  import { HttpUserPoolAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
524
554
  import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
525
555
  import { IUserPool, IUserPoolClient } from 'aws-cdk-lib/aws-cognito';
@@ -632,6 +662,48 @@ export class TestApi<
632
662
  ...props,
633
663
  });
634
664
  }
665
+
666
+ /**
667
+ * Restricts CORS to the website CloudFront distribution domains
668
+ *
669
+ * Configures the CloudFront distribution domains as the only permitted CORS origins
670
+ * (other than local host with default ports) in the API gateway
671
+ * The CORS origins are not configured within the AWS Lambda integrations since
672
+ * the associated header is controlled by API Gateway v2
673
+ *
674
+ * @param cloudFrontDistribution - The CloudFront distribution to grant CORS from
675
+ */
676
+ public restrictCorsTo(
677
+ ...websites: { cloudFrontDistribution: Distribution }[]
678
+ ) {
679
+ const allowedOrigins = websites.map(
680
+ ({ cloudFrontDistribution }) =>
681
+ \`https://\${cloudFrontDistribution.distributionDomainName}\`,
682
+ );
683
+
684
+ const cfnApi = this.api.node.defaultChild;
685
+ if (!(cfnApi instanceof CfnApi)) {
686
+ throw new Error(
687
+ 'Unable to configure CORS: API default child is not a CfnApi instance',
688
+ );
689
+ }
690
+
691
+ cfnApi.corsConfiguration = {
692
+ allowOrigins: [
693
+ 'http://localhost:4200',
694
+ 'http://localhost:4300',
695
+ ...allowedOrigins,
696
+ ],
697
+ allowMethods: [CorsHttpMethod.ANY],
698
+ allowHeaders: [
699
+ 'authorization',
700
+ 'content-type',
701
+ 'x-amz-content-sha256',
702
+ 'x-amz-date',
703
+ 'x-amz-security-token',
704
+ ],
705
+ };
706
+ }
635
707
  }
636
708
  "
637
709
  `;
@@ -663,6 +735,7 @@ exports[`trpc backend generator > should generate with no auth for a REST API >
663
735
  exports[`trpc backend generator > should generate with no auth for a REST API > packages/common/constructs/src/app/apis/test-api.ts 1`] = `
664
736
  "import { Construct } from 'constructs';
665
737
  import * as url from 'url';
738
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
666
739
  import {
667
740
  Code,
668
741
  Runtime,
@@ -781,6 +854,34 @@ export class TestApi<
781
854
  ...props,
782
855
  });
783
856
  }
857
+
858
+ /**
859
+ * Restricts CORS to the website CloudFront distribution domains
860
+ *
861
+ * Configures the CloudFront distribution domains as the only permitted CORS origins
862
+ * (other than local host) in the AWS Lambda integrations
863
+ *
864
+ * Note that this restriction is not applied to preflight OPTIONS
865
+ *
866
+ * @param websites - The CloudFront distribution to grant CORS from
867
+ */
868
+ public restrictCorsTo(
869
+ ...websites: { cloudFrontDistribution: Distribution }[]
870
+ ) {
871
+ const allowedOrigins = websites
872
+ .map(
873
+ ({ cloudFrontDistribution }) =>
874
+ \`https://\${cloudFrontDistribution.distributionDomainName}\`,
875
+ )
876
+ .join(',');
877
+
878
+ // Set ALLOWED_ORIGINS environment variable for all Lambda integrations
879
+ Object.values(this.integrations).forEach((integration) => {
880
+ if ('handler' in integration && integration.handler instanceof Function) {
881
+ integration.handler.addEnvironment('ALLOWED_ORIGINS', allowedOrigins);
882
+ }
883
+ });
884
+ }
784
885
  }
785
886
  "
786
887
  `;
@@ -812,6 +913,7 @@ exports[`trpc backend generator > should generate with no auth for an HTTP API >
812
913
  exports[`trpc backend generator > should generate with no auth for an HTTP API > packages/common/constructs/src/app/apis/test-api.ts 1`] = `
813
914
  "import { Construct } from 'constructs';
814
915
  import * as url from 'url';
916
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
815
917
  import {
816
918
  Code,
817
919
  Runtime,
@@ -822,6 +924,7 @@ import {
822
924
  import { Duration } from 'aws-cdk-lib';
823
925
  import {
824
926
  CorsHttpMethod,
927
+ CfnApi,
825
928
  HttpNoneAuthorizer,
826
929
  } from 'aws-cdk-lib/aws-apigatewayv2';
827
930
  import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
@@ -921,6 +1024,48 @@ export class TestApi<
921
1024
  ...props,
922
1025
  });
923
1026
  }
1027
+
1028
+ /**
1029
+ * Restricts CORS to the website CloudFront distribution domains
1030
+ *
1031
+ * Configures the CloudFront distribution domains as the only permitted CORS origins
1032
+ * (other than local host with default ports) in the API gateway
1033
+ * The CORS origins are not configured within the AWS Lambda integrations since
1034
+ * the associated header is controlled by API Gateway v2
1035
+ *
1036
+ * @param cloudFrontDistribution - The CloudFront distribution to grant CORS from
1037
+ */
1038
+ public restrictCorsTo(
1039
+ ...websites: { cloudFrontDistribution: Distribution }[]
1040
+ ) {
1041
+ const allowedOrigins = websites.map(
1042
+ ({ cloudFrontDistribution }) =>
1043
+ \`https://\${cloudFrontDistribution.distributionDomainName}\`,
1044
+ );
1045
+
1046
+ const cfnApi = this.api.node.defaultChild;
1047
+ if (!(cfnApi instanceof CfnApi)) {
1048
+ throw new Error(
1049
+ 'Unable to configure CORS: API default child is not a CfnApi instance',
1050
+ );
1051
+ }
1052
+
1053
+ cfnApi.corsConfiguration = {
1054
+ allowOrigins: [
1055
+ 'http://localhost:4200',
1056
+ 'http://localhost:4300',
1057
+ ...allowedOrigins,
1058
+ ],
1059
+ allowMethods: [CorsHttpMethod.ANY],
1060
+ allowHeaders: [
1061
+ 'authorization',
1062
+ 'content-type',
1063
+ 'x-amz-content-sha256',
1064
+ 'x-amz-date',
1065
+ 'x-amz-security-token',
1066
+ ],
1067
+ };
1068
+ }
924
1069
  }
925
1070
  "
926
1071
  `;
@@ -1065,6 +1210,7 @@ export class HttpApi<
1065
1210
  exports[`trpc backend generator > should set up shared constructs for http > test-api.ts 1`] = `
1066
1211
  "import { Construct } from 'constructs';
1067
1212
  import * as url from 'url';
1213
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
1068
1214
  import {
1069
1215
  Code,
1070
1216
  Runtime,
@@ -1073,7 +1219,7 @@ import {
1073
1219
  Tracing,
1074
1220
  } from 'aws-cdk-lib/aws-lambda';
1075
1221
  import { Duration } from 'aws-cdk-lib';
1076
- import { CorsHttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
1222
+ import { CorsHttpMethod, CfnApi } from 'aws-cdk-lib/aws-apigatewayv2';
1077
1223
  import { HttpIamAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
1078
1224
  import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
1079
1225
  import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
@@ -1174,6 +1320,48 @@ export class TestApi<
1174
1320
  });
1175
1321
  }
1176
1322
 
1323
+ /**
1324
+ * Restricts CORS to the website CloudFront distribution domains
1325
+ *
1326
+ * Configures the CloudFront distribution domains as the only permitted CORS origins
1327
+ * (other than local host with default ports) in the API gateway
1328
+ * The CORS origins are not configured within the AWS Lambda integrations since
1329
+ * the associated header is controlled by API Gateway v2
1330
+ *
1331
+ * @param cloudFrontDistribution - The CloudFront distribution to grant CORS from
1332
+ */
1333
+ public restrictCorsTo(
1334
+ ...websites: { cloudFrontDistribution: Distribution }[]
1335
+ ) {
1336
+ const allowedOrigins = websites.map(
1337
+ ({ cloudFrontDistribution }) =>
1338
+ \`https://\${cloudFrontDistribution.distributionDomainName}\`,
1339
+ );
1340
+
1341
+ const cfnApi = this.api.node.defaultChild;
1342
+ if (!(cfnApi instanceof CfnApi)) {
1343
+ throw new Error(
1344
+ 'Unable to configure CORS: API default child is not a CfnApi instance',
1345
+ );
1346
+ }
1347
+
1348
+ cfnApi.corsConfiguration = {
1349
+ allowOrigins: [
1350
+ 'http://localhost:4200',
1351
+ 'http://localhost:4300',
1352
+ ...allowedOrigins,
1353
+ ],
1354
+ allowMethods: [CorsHttpMethod.ANY],
1355
+ allowHeaders: [
1356
+ 'authorization',
1357
+ 'content-type',
1358
+ 'x-amz-content-sha256',
1359
+ 'x-amz-date',
1360
+ 'x-amz-security-token',
1361
+ ],
1362
+ };
1363
+ }
1364
+
1177
1365
  /**
1178
1366
  * Grants IAM permissions to invoke any method on this API.
1179
1367
  *
@@ -1627,6 +1815,7 @@ export class RestApi<
1627
1815
  exports[`trpc backend generator > should set up shared constructs for rest > test-api.ts 1`] = `
1628
1816
  "import { Construct } from 'constructs';
1629
1817
  import * as url from 'url';
1818
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
1630
1819
  import {
1631
1820
  Code,
1632
1821
  Runtime,
@@ -1748,6 +1937,34 @@ export class TestApi<
1748
1937
  });
1749
1938
  }
1750
1939
 
1940
+ /**
1941
+ * Restricts CORS to the website CloudFront distribution domains
1942
+ *
1943
+ * Configures the CloudFront distribution domains as the only permitted CORS origins
1944
+ * (other than local host) in the AWS Lambda integrations
1945
+ *
1946
+ * Note that this restriction is not applied to preflight OPTIONS
1947
+ *
1948
+ * @param websites - The CloudFront distribution to grant CORS from
1949
+ */
1950
+ public restrictCorsTo(
1951
+ ...websites: { cloudFrontDistribution: Distribution }[]
1952
+ ) {
1953
+ const allowedOrigins = websites
1954
+ .map(
1955
+ ({ cloudFrontDistribution }) =>
1956
+ \`https://\${cloudFrontDistribution.distributionDomainName}\`,
1957
+ )
1958
+ .join(',');
1959
+
1960
+ // Set ALLOWED_ORIGINS environment variable for all Lambda integrations
1961
+ Object.values(this.integrations).forEach((integration) => {
1962
+ if ('handler' in integration && integration.handler instanceof Function) {
1963
+ integration.handler.addEnvironment('ALLOWED_ORIGINS', allowedOrigins);
1964
+ }
1965
+ });
1966
+ }
1967
+
1751
1968
  /**
1752
1969
  * Grants IAM permissions to invoke any method on this API.
1753
1970
  *
@@ -16,13 +16,30 @@ export const handler = awsLambdaRequestHandler({
16
16
  router: appRouter,
17
17
  createContext: (ctx: CreateAWSLambdaContextOptions<<%- apiGatewayEventType %>>) => ctx,
18
18
  <%_ if (computeType === 'ServerlessApiGatewayRestApi') { _%>
19
- responseMeta: () => ({
20
- headers: {
21
- 'Access-Control-Allow-Origin': '*',
22
- 'Access-Control-Allow-Methods': '*',
23
- },
24
- }),
19
+ responseMeta: ({ ctx }) => {
20
+ return {
21
+ headers: {
22
+ 'Access-Control-Allow-Origin': getAllowedOrigin(ctx?.event),
23
+ 'Access-Control-Allow-Methods': '*',
24
+ },
25
+ };
26
+ },
25
27
  <%_ } _%>
26
28
  });
27
29
 
30
+ <%_ if (computeType === 'ServerlessApiGatewayRestApi') { _%>
31
+ const getAllowedOrigin = (event: <%- apiGatewayEventType %> | undefined) => {
32
+ const origin = event?.headers?.origin ?? event?.headers?.Origin;
33
+ const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') ?? [];
34
+ const isLocalHost =
35
+ origin && new Set(['localhost', '127.0.0.1']).has(new URL(origin).hostname);
36
+ const isAllowedOrigin = origin && allowedOrigins.includes(origin);
37
+ let corsOrigin = '*';
38
+ if (allowedOrigins.length > 0 && !isLocalHost) {
39
+ corsOrigin = isAllowedOrigin ? origin : allowedOrigins[0];
40
+ }
41
+ return corsOrigin;
42
+ };
43
+ <%_ } _%>
44
+
28
45
  export type AppRouter = typeof appRouter;
@@ -1,5 +1,6 @@
1
1
  import { Construct } from 'constructs';
2
2
  import * as url from 'url';
3
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
3
4
  import {
4
5
  Code,
5
6
  Runtime,
@@ -10,6 +11,7 @@ import {
10
11
  import { Duration } from 'aws-cdk-lib';
11
12
  import {
12
13
  CorsHttpMethod,
14
+ CfnApi,
13
15
  <%_ if (auth === 'None') { _%>
14
16
  HttpNoneAuthorizer,
15
17
  <%_ } _%>
@@ -168,6 +170,49 @@ export class <%= apiNameClassName %><
168
170
  ...props,
169
171
  });
170
172
  }
173
+
174
+ /**
175
+ * Restricts CORS to the website CloudFront distribution domains
176
+ *
177
+ * Configures the CloudFront distribution domains as the only permitted CORS origins
178
+ * (other than local host with default ports) in the API gateway
179
+ * The CORS origins are not configured within the AWS Lambda integrations since
180
+ * the associated header is controlled by API Gateway v2
181
+ *
182
+ * @param cloudFrontDistribution - The CloudFront distribution to grant CORS from
183
+ */
184
+ public restrictCorsTo(
185
+ ...websites: { cloudFrontDistribution: Distribution }[]
186
+ ) {
187
+ const allowedOrigins = websites
188
+ .map(
189
+ ({ cloudFrontDistribution }) =>
190
+ `https://${cloudFrontDistribution.distributionDomainName}`,
191
+ );
192
+
193
+ const cfnApi = this.api.node.defaultChild;
194
+ if (!(cfnApi instanceof CfnApi)) {
195
+ throw new Error(
196
+ 'Unable to configure CORS: API default child is not a CfnApi instance',
197
+ );
198
+ }
199
+
200
+ cfnApi.corsConfiguration = {
201
+ allowOrigins: [
202
+ 'http://localhost:4200',
203
+ 'http://localhost:4300',
204
+ ...allowedOrigins,
205
+ ],
206
+ allowMethods: [CorsHttpMethod.ANY],
207
+ allowHeaders: [
208
+ 'authorization',
209
+ 'content-type',
210
+ 'x-amz-content-sha256',
211
+ 'x-amz-date',
212
+ 'x-amz-security-token',
213
+ ],
214
+ };
215
+ }
171
216
  <%_ if (auth === 'IAM') { _%>
172
217
 
173
218
  /**
@@ -1,5 +1,6 @@
1
1
  import { Construct } from 'constructs';
2
2
  import * as url from 'url';
3
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
3
4
  import {
4
5
  Code,
5
6
  Runtime,
@@ -183,6 +184,34 @@ export class <%= apiNameClassName %><
183
184
  ...props,
184
185
  });
185
186
  }
187
+
188
+ /**
189
+ * Restricts CORS to the website CloudFront distribution domains
190
+ *
191
+ * Configures the CloudFront distribution domains as the only permitted CORS origins
192
+ * (other than local host) in the AWS Lambda integrations
193
+ *
194
+ * Note that this restriction is not applied to preflight OPTIONS
195
+ *
196
+ * @param websites - The CloudFront distribution to grant CORS from
197
+ */
198
+ public restrictCorsTo(
199
+ ...websites: { cloudFrontDistribution: Distribution }[]
200
+ ) {
201
+ const allowedOrigins = websites
202
+ .map(
203
+ ({ cloudFrontDistribution }) =>
204
+ `https://${cloudFrontDistribution.distributionDomainName}`,
205
+ )
206
+ .join(',');
207
+
208
+ // Set ALLOWED_ORIGINS environment variable for all Lambda integrations
209
+ Object.values(this.integrations).forEach((integration) => {
210
+ if ('handler' in integration && integration.handler instanceof Function) {
211
+ integration.handler.addEnvironment('ALLOWED_ORIGINS', allowedOrigins);
212
+ }
213
+ });
214
+ }
186
215
  <%_ if (auth === 'IAM') { _%>
187
216
 
188
217
  /**