@aws/nx-plugin 0.46.0 → 0.48.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 +1 -1
- package/src/infra/app/generator.js +4 -1
- package/src/infra/app/generator.js.map +1 -1
- package/src/mcp-server/tools/create-workspace-command.js +4 -0
- package/src/mcp-server/tools/create-workspace-command.js.map +1 -1
- package/src/mcp-server/tools/general-guidance.js +3 -1
- package/src/mcp-server/tools/general-guidance.js.map +1 -1
- package/src/preset/__snapshots__/generator.spec.ts.snap +9 -0
- package/src/preset/generator.d.ts +1 -1
- package/src/preset/generator.js +4 -1
- package/src/preset/generator.js.map +1 -1
- package/src/preset/schema.d.ts +3 -0
- package/src/preset/schema.json +9 -1
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +60 -0
- package/src/py/fast-api/generator.js +5 -3
- package/src/py/fast-api/generator.js.map +1 -1
- package/src/py/fast-api/react/__snapshots__/generator.spec.ts.snap +1 -0
- package/src/py/fast-api/schema.d.ts +3 -1
- package/src/py/fast-api/schema.json +4 -4
- package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +310 -0
- package/src/py/lambda-function/generator.js +17 -25
- package/src/py/lambda-function/generator.js.map +1 -1
- package/src/py/lambda-function/schema.d.ts +3 -0
- package/src/py/lambda-function/schema.json +8 -0
- package/src/py/mcp-server/generator.js +4 -2
- package/src/py/mcp-server/generator.js.map +1 -1
- package/src/py/mcp-server/schema.d.ts +4 -1
- package/src/py/mcp-server/schema.json +4 -4
- package/src/py/strands-agent/generator.js +4 -2
- package/src/py/strands-agent/generator.js.map +1 -1
- package/src/py/strands-agent/schema.d.ts +3 -1
- package/src/py/strands-agent/schema.json +4 -4
- package/src/terraform/project/generator.js +2 -1
- package/src/terraform/project/generator.js.map +1 -1
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +60 -0
- package/src/trpc/backend/generator.js +4 -2
- package/src/trpc/backend/generator.js.map +1 -1
- package/src/trpc/backend/schema.d.ts +2 -1
- package/src/trpc/backend/schema.json +4 -4
- package/src/trpc/react/__snapshots__/generator.spec.ts.snap +1 -0
- package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +310 -0
- package/src/ts/lambda-function/generator.js +17 -27
- package/src/ts/lambda-function/generator.js.map +1 -1
- package/src/ts/lambda-function/schema.d.ts +3 -0
- package/src/ts/lambda-function/schema.json +8 -0
- package/src/ts/mcp-server/generator.js +4 -2
- package/src/ts/mcp-server/generator.js.map +1 -1
- package/src/ts/mcp-server/schema.d.ts +3 -1
- package/src/ts/mcp-server/schema.json +4 -4
- package/src/ts/nx-generator/__snapshots__/generator.spec.ts.snap +27 -1
- package/src/ts/nx-generator/files/nx-plugin-for-aws/docs/__nameKebabCase__.mdx.template +31 -0
- package/src/ts/nx-generator/files/nx-plugin-for-aws/generator/generator.spec.ts.template +1 -1
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +756 -0
- package/src/ts/react-website/app/generator.js +44 -43
- package/src/ts/react-website/app/generator.js.map +1 -1
- package/src/ts/react-website/app/schema.d.ts +2 -0
- package/src/ts/react-website/app/schema.json +8 -0
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +259 -0
- package/src/ts/react-website/cognito-auth/generator.js +12 -13
- package/src/ts/react-website/cognito-auth/generator.js.map +1 -1
- package/src/ts/react-website/cognito-auth/schema.d.ts +4 -0
- package/src/ts/react-website/cognito-auth/schema.json +8 -0
- package/src/ts/react-website/runtime-config/__snapshots__/generator.spec.ts.snap +0 -40
- package/src/ts/react-website/runtime-config/generator.js +0 -2
- package/src/ts/react-website/runtime-config/generator.js.map +1 -1
- package/src/utils/agent-core-constructs/agent-core-constructs.d.ts +4 -2
- package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
- package/src/utils/api-constructs/api-constructs.d.ts +2 -1
- package/src/utils/api-constructs/api-constructs.js.map +1 -1
- package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +10 -0
- package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +10 -0
- package/src/utils/config/index.d.ts +6 -0
- package/src/utils/config/index.js.map +1 -1
- package/src/utils/files/terraform/src/core/runtime-config/entry/entry.tf.template +119 -0
- package/src/utils/files/terraform/src/core/runtime-config/read/read.tf.template +28 -0
- package/src/utils/files/terraform/src/metrics/metrics.tf.template +7 -1
- package/src/{py/lambda-function/files/common/constructs/src/app/lambda-functions/__constructFunctionKebabCase__.ts.template → utils/function-constructs/files/cdk/app/lambda-functions/__functionNameKebabCase__.ts.template} +5 -5
- package/src/utils/function-constructs/files/terraform/app/lambda-functions/__functionNameKebabCase__/__functionNameKebabCase__.tf.template +150 -0
- package/src/utils/function-constructs/function-constructs.d.ts +20 -0
- package/src/utils/function-constructs/function-constructs.js +57 -0
- package/src/utils/function-constructs/function-constructs.js.map +1 -0
- package/src/utils/iac.d.ts +21 -0
- package/src/utils/iac.js +25 -0
- package/src/utils/iac.js.map +1 -0
- package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +123 -0
- package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +421 -0
- package/src/utils/identity-constructs/files/terraform/core/user-identity/main.tf.template +47 -0
- package/src/utils/identity-constructs/identity-constructs.d.ts +16 -0
- package/src/utils/identity-constructs/identity-constructs.js +84 -0
- package/src/utils/identity-constructs/identity-constructs.js.map +1 -0
- package/src/utils/metrics.js +1 -1
- package/src/utils/metrics.js.map +1 -1
- package/src/utils/shared-constructs.d.ts +3 -2
- package/src/utils/shared-constructs.js +27 -3
- package/src/utils/shared-constructs.js.map +1 -1
- package/src/utils/website-constructs/files/terraform/app/static-websites/__websiteNameKebabCase__/__websiteNameKebabCase__.tf.template +42 -0
- package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +709 -0
- package/src/utils/website-constructs/website-constructs.d.ts +19 -0
- package/src/utils/website-constructs/website-constructs.js +61 -0
- package/src/utils/website-constructs/website-constructs.js.map +1 -0
- package/src/ts/lambda-function/files/common/constructs/src/app/lambda-functions/__constructFunctionNameKebabCase__.ts.template +0 -24
- /package/src/{ts/react-website/cognito-auth/files/common/constructs/src → utils/identity-constructs/files/cdk}/core/user-identity.ts.template +0 -0
- /package/src/{ts/react-website/app/files/common/constructs/src → utils/website-constructs/files/cdk}/app/static-websites/__websiteNameKebabCase__.ts.template +0 -0
- /package/src/{ts/react-website/app/files/common/constructs/src → utils/website-constructs/files/cdk}/core/static-website.ts.template +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_providers {
|
|
3
|
+
aws = {
|
|
4
|
+
source = "hashicorp/aws"
|
|
5
|
+
version = "~> 6.0"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
# Variables
|
|
11
|
+
variable "callback_url" {
|
|
12
|
+
description = "Callback URL to add (e.g., https://d123456789.cloudfront.net)"
|
|
13
|
+
type = string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
# Read runtime config to get user pool client details
|
|
17
|
+
module "runtime_config_reader" {
|
|
18
|
+
source = "../../runtime-config/read"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Extract cognito details from runtime config
|
|
22
|
+
locals {
|
|
23
|
+
cognito_props = try(module.runtime_config_reader.config.cognitoProps, null)
|
|
24
|
+
|
|
25
|
+
user_pool_id = local.cognito_props != null ? local.cognito_props.userPoolId : null
|
|
26
|
+
user_pool_client_id = local.cognito_props != null ? local.cognito_props.userPoolWebClientId : null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Validation: Ensure cognito props exist in runtime config
|
|
30
|
+
resource "terraform_data" "validate_cognito_props" {
|
|
31
|
+
lifecycle {
|
|
32
|
+
precondition {
|
|
33
|
+
condition = local.cognito_props != null
|
|
34
|
+
error_message = "ERROR: cognitoProps not found in runtime config. Ensure user-identity module has been deployed first and has added cognitoProps to the runtime configuration."
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
precondition {
|
|
38
|
+
condition = local.user_pool_id != null && local.user_pool_id != ""
|
|
39
|
+
error_message = "ERROR: cognitoProps.userPoolId is missing or empty in runtime config. Check that user-identity module completed successfully."
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
precondition {
|
|
43
|
+
condition = local.user_pool_client_id != null && local.user_pool_client_id != ""
|
|
44
|
+
error_message = "ERROR: cognitoProps.userPoolWebClientId is missing or empty in runtime config. Check that user-identity module completed successfully."
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Update the user pool client with additional callback URL
|
|
51
|
+
resource "null_resource" "add_callback_url" {
|
|
52
|
+
triggers = {
|
|
53
|
+
callback_url = var.callback_url
|
|
54
|
+
user_pool_id = local.user_pool_id
|
|
55
|
+
user_pool_client_id = local.user_pool_client_id
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
provisioner "local-exec" {
|
|
59
|
+
command = <<-EOT
|
|
60
|
+
uv run --with boto3 python -c "
|
|
61
|
+
import boto3
|
|
62
|
+
import sys
|
|
63
|
+
|
|
64
|
+
# Configuration
|
|
65
|
+
user_pool_id = '${local.user_pool_id}'
|
|
66
|
+
client_id = '${local.user_pool_client_id}'
|
|
67
|
+
new_callback_url = '${var.callback_url}'
|
|
68
|
+
|
|
69
|
+
# Initialize Cognito client
|
|
70
|
+
cognito = boto3.client('cognito-idp')
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
# Get current user pool client configuration
|
|
74
|
+
response = cognito.describe_user_pool_client(
|
|
75
|
+
UserPoolId=user_pool_id,
|
|
76
|
+
ClientId=client_id
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
client_config = response['UserPoolClient']
|
|
80
|
+
current_callback_urls = client_config.get('CallbackURLs', [])
|
|
81
|
+
current_logout_urls = client_config.get('LogoutURLs', [])
|
|
82
|
+
|
|
83
|
+
# Check if URL already exists
|
|
84
|
+
if new_callback_url in current_callback_urls:
|
|
85
|
+
print(f'Callback URL {new_callback_url} already exists')
|
|
86
|
+
else:
|
|
87
|
+
# Add new URL to both callback and logout URLs
|
|
88
|
+
updated_callback_urls = current_callback_urls + [new_callback_url]
|
|
89
|
+
updated_logout_urls = current_logout_urls + [new_callback_url]
|
|
90
|
+
|
|
91
|
+
# Update the user pool client
|
|
92
|
+
# Only include valid update parameters (exclude read-only fields and ones we're setting)
|
|
93
|
+
valid_update_params = [
|
|
94
|
+
'ClientName', 'RefreshTokenValidity', 'AccessTokenValidity', 'IdTokenValidity',
|
|
95
|
+
'TokenValidityUnits', 'ReadAttributes', 'WriteAttributes', 'ExplicitAuthFlows',
|
|
96
|
+
'SupportedIdentityProviders', 'DefaultRedirectURI', 'AllowedOAuthFlows',
|
|
97
|
+
'AllowedOAuthScopes', 'AllowedOAuthFlowsUserPoolClient', 'AnalyticsConfiguration',
|
|
98
|
+
'PreventUserExistenceErrors', 'EnableTokenRevocation',
|
|
99
|
+
'EnablePropagateAdditionalUserContextData', 'AuthSessionValidity', 'RefreshTokenRotation'
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
update_config = {k: v for k, v in client_config.items() if k in valid_update_params}
|
|
103
|
+
|
|
104
|
+
cognito.update_user_pool_client(
|
|
105
|
+
UserPoolId=user_pool_id,
|
|
106
|
+
ClientId=client_id,
|
|
107
|
+
CallbackURLs=updated_callback_urls,
|
|
108
|
+
LogoutURLs=updated_logout_urls,
|
|
109
|
+
**update_config
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
print(f'Successfully added callback URL: {new_callback_url}')
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
print(f'Error updating callback URLs: {e}')
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
"
|
|
118
|
+
EOT
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
depends_on = [terraform_data.validate_cognito_props]
|
|
122
|
+
}
|
|
123
|
+
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_providers {
|
|
3
|
+
aws = {
|
|
4
|
+
source = "hashicorp/aws"
|
|
5
|
+
version = "~> 6.0"
|
|
6
|
+
}
|
|
7
|
+
random = {
|
|
8
|
+
source = "hashicorp/random"
|
|
9
|
+
version = "~> 3.1"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
# Variables
|
|
15
|
+
variable "user_pool_domain_prefix" {
|
|
16
|
+
description = "Prefix for the Cognito User Pool domain"
|
|
17
|
+
type = string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
variable "allow_signup" {
|
|
21
|
+
description = "Set to true to allow users to sign themselves up"
|
|
22
|
+
type = bool
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
variable "callback_urls" {
|
|
26
|
+
description = "Additional callback URLs for the user pool client"
|
|
27
|
+
type = list(string)
|
|
28
|
+
default = []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
variable "logout_urls" {
|
|
32
|
+
description = "Additional logout URLs for the user pool client"
|
|
33
|
+
type = list(string)
|
|
34
|
+
default = []
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Data sources
|
|
38
|
+
data "aws_caller_identity" "current" {}
|
|
39
|
+
data "aws_region" "current" {}
|
|
40
|
+
|
|
41
|
+
# Random suffix for resource names
|
|
42
|
+
resource "random_id" "unique_suffix" {
|
|
43
|
+
byte_length = 4
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Generate a random external ID for SMS role security
|
|
47
|
+
resource "random_uuid" "sms_external_id" {}
|
|
48
|
+
|
|
49
|
+
# IAM role for SMS MFA
|
|
50
|
+
resource "aws_iam_role" "cognito_sms_role" {
|
|
51
|
+
name = "cognito-sms-role-${random_id.unique_suffix.hex}"
|
|
52
|
+
|
|
53
|
+
assume_role_policy = jsonencode({
|
|
54
|
+
Version = "2012-10-17"
|
|
55
|
+
Statement = [
|
|
56
|
+
{
|
|
57
|
+
Action = "sts:AssumeRole"
|
|
58
|
+
Effect = "Allow"
|
|
59
|
+
Principal = {
|
|
60
|
+
Service = "cognito-idp.amazonaws.com"
|
|
61
|
+
}
|
|
62
|
+
Condition = {
|
|
63
|
+
StringEquals = {
|
|
64
|
+
"sts:ExternalId" = random_uuid.sms_external_id.result
|
|
65
|
+
"aws:SourceAccount" = data.aws_caller_identity.current.account_id
|
|
66
|
+
}
|
|
67
|
+
ArnLike = {
|
|
68
|
+
"aws:SourceArn" = "arn:aws:cognito-idp:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:userpool/*"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
resource "aws_iam_role_policy" "cognito_sms_policy" {
|
|
77
|
+
# checkov:skip=CKV_AWS_290:Cognito SMS requires sns:Publish with wildcard resource
|
|
78
|
+
# checkov:skip=CKV_AWS_355:Cognito SMS requires sns:Publish with wildcard resource
|
|
79
|
+
name = "cognito-sms-policy-${random_id.unique_suffix.hex}"
|
|
80
|
+
role = aws_iam_role.cognito_sms_role.id
|
|
81
|
+
|
|
82
|
+
policy = jsonencode({
|
|
83
|
+
Version = "2012-10-17"
|
|
84
|
+
Statement = [
|
|
85
|
+
{
|
|
86
|
+
Effect = "Allow"
|
|
87
|
+
Action = [
|
|
88
|
+
"sns:Publish"
|
|
89
|
+
]
|
|
90
|
+
Resource = "*"
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# Cognito User Pool
|
|
97
|
+
resource "aws_cognito_user_pool" "user_pool" {
|
|
98
|
+
name = "UserPool-${random_id.unique_suffix.hex}"
|
|
99
|
+
deletion_protection = "ACTIVE"
|
|
100
|
+
|
|
101
|
+
admin_create_user_config {
|
|
102
|
+
allow_admin_create_user_only = !var.allow_signup
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Password policy
|
|
106
|
+
password_policy {
|
|
107
|
+
minimum_length = 8
|
|
108
|
+
require_lowercase = true
|
|
109
|
+
require_uppercase = true
|
|
110
|
+
require_numbers = true
|
|
111
|
+
require_symbols = true
|
|
112
|
+
temporary_password_validity_days = 3
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# MFA configuration
|
|
116
|
+
mfa_configuration = "ON"
|
|
117
|
+
|
|
118
|
+
# Software token MFA configuration
|
|
119
|
+
software_token_mfa_configuration {
|
|
120
|
+
enabled = true
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# SMS MFA configuration
|
|
124
|
+
sms_configuration {
|
|
125
|
+
external_id = random_uuid.sms_external_id.result
|
|
126
|
+
sns_caller_arn = aws_iam_role.cognito_sms_role.arn
|
|
127
|
+
sns_region = data.aws_region.current.name
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
depends_on = [aws_iam_role_policy.cognito_sms_policy]
|
|
131
|
+
|
|
132
|
+
# Sign-in configuration
|
|
133
|
+
username_configuration {
|
|
134
|
+
case_sensitive = false
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
alias_attributes = ["email"]
|
|
138
|
+
|
|
139
|
+
# Account recovery
|
|
140
|
+
account_recovery_setting {
|
|
141
|
+
recovery_mechanism {
|
|
142
|
+
name = "verified_email"
|
|
143
|
+
priority = 1
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Auto verification
|
|
148
|
+
auto_verified_attributes = ["email", "phone_number"]
|
|
149
|
+
|
|
150
|
+
# User pool add-ons (equivalent to FeaturePlan.ESSENTIALS)
|
|
151
|
+
user_pool_add_ons {
|
|
152
|
+
advanced_security_mode = "ENFORCED"
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# Schema attributes
|
|
156
|
+
schema {
|
|
157
|
+
attribute_data_type = "String"
|
|
158
|
+
name = "email"
|
|
159
|
+
required = true
|
|
160
|
+
mutable = true
|
|
161
|
+
|
|
162
|
+
string_attribute_constraints {
|
|
163
|
+
min_length = 1
|
|
164
|
+
max_length = 256
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
schema {
|
|
169
|
+
attribute_data_type = "String"
|
|
170
|
+
name = "given_name"
|
|
171
|
+
required = true
|
|
172
|
+
mutable = true
|
|
173
|
+
|
|
174
|
+
string_attribute_constraints {
|
|
175
|
+
min_length = 1
|
|
176
|
+
max_length = 256
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
schema {
|
|
181
|
+
attribute_data_type = "String"
|
|
182
|
+
name = "family_name"
|
|
183
|
+
required = true
|
|
184
|
+
mutable = true
|
|
185
|
+
|
|
186
|
+
string_attribute_constraints {
|
|
187
|
+
min_length = 1
|
|
188
|
+
max_length = 256
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
schema {
|
|
193
|
+
attribute_data_type = "String"
|
|
194
|
+
name = "phone_number"
|
|
195
|
+
required = false
|
|
196
|
+
mutable = true
|
|
197
|
+
|
|
198
|
+
string_attribute_constraints {
|
|
199
|
+
min_length = 1
|
|
200
|
+
max_length = 256
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# User attribute update settings - require verification before update
|
|
205
|
+
user_attribute_update_settings {
|
|
206
|
+
attributes_require_verification_before_update = ["email", "phone_number"]
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
# Verification message templates
|
|
210
|
+
verification_message_template {
|
|
211
|
+
default_email_option = "CONFIRM_WITH_CODE"
|
|
212
|
+
email_message = "The verification code to your new account is {####}"
|
|
213
|
+
email_subject = "Verify your new account"
|
|
214
|
+
sms_message = "The verification code to your new account is {####}"
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
tags = {
|
|
218
|
+
Name = "UserPool-${random_id.unique_suffix.hex}"
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# User Pool Domain - temporarily commented out to resolve domain conflicts
|
|
223
|
+
# Will be re-enabled after User Pool Client OAuth configuration is fixed
|
|
224
|
+
resource "aws_cognito_user_pool_domain" "user_pool_domain" {
|
|
225
|
+
domain = "${var.user_pool_domain_prefix}-${data.aws_caller_identity.current.account_id}-${random_id.unique_suffix.hex}"
|
|
226
|
+
user_pool_id = aws_cognito_user_pool.user_pool.id
|
|
227
|
+
managed_login_version = 2
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
# User Pool Client
|
|
231
|
+
resource "aws_cognito_user_pool_client" "web_client" {
|
|
232
|
+
name = "WebClient-${random_id.unique_suffix.hex}"
|
|
233
|
+
user_pool_id = aws_cognito_user_pool.user_pool.id
|
|
234
|
+
|
|
235
|
+
# Auth flows - match CDK implementation (order matches CloudFormation)
|
|
236
|
+
explicit_auth_flows = [
|
|
237
|
+
"ALLOW_REFRESH_TOKEN_AUTH",
|
|
238
|
+
"ALLOW_USER_AUTH",
|
|
239
|
+
"ALLOW_USER_PASSWORD_AUTH",
|
|
240
|
+
"ALLOW_USER_SRP_AUTH"
|
|
241
|
+
]
|
|
242
|
+
|
|
243
|
+
# Supported identity providers
|
|
244
|
+
supported_identity_providers = ["COGNITO"]
|
|
245
|
+
|
|
246
|
+
# OAuth configuration - MUST be set before callback/logout URLs
|
|
247
|
+
allowed_oauth_flows_user_pool_client = true
|
|
248
|
+
allowed_oauth_flows = ["code"]
|
|
249
|
+
allowed_oauth_scopes = ["email", "openid", "profile"]
|
|
250
|
+
|
|
251
|
+
# OAuth-dependent URLs - set after OAuth configuration
|
|
252
|
+
callback_urls = concat([
|
|
253
|
+
"http://localhost:4200",
|
|
254
|
+
"http://localhost:4300",
|
|
255
|
+
"https://${data.aws_region.current.name}.console.aws.amazon.com"
|
|
256
|
+
], var.callback_urls)
|
|
257
|
+
|
|
258
|
+
logout_urls = concat([
|
|
259
|
+
"http://localhost:4200",
|
|
260
|
+
"http://localhost:4300",
|
|
261
|
+
"https://${data.aws_region.current.name}.console.aws.amazon.com"
|
|
262
|
+
], var.logout_urls)
|
|
263
|
+
|
|
264
|
+
# Security settings
|
|
265
|
+
prevent_user_existence_errors = "ENABLED"
|
|
266
|
+
enable_token_revocation = true
|
|
267
|
+
enable_propagate_additional_user_context_data = false
|
|
268
|
+
|
|
269
|
+
# Token validity - ONLY refresh token to match CloudFormation exactly
|
|
270
|
+
refresh_token_validity = 30
|
|
271
|
+
|
|
272
|
+
# Auth session validity
|
|
273
|
+
auth_session_validity = 3
|
|
274
|
+
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
# Identity Pool
|
|
278
|
+
resource "aws_cognito_identity_pool" "identity_pool" {
|
|
279
|
+
identity_pool_name = "IdentityPool-${random_id.unique_suffix.hex}"
|
|
280
|
+
allow_unauthenticated_identities = false
|
|
281
|
+
|
|
282
|
+
cognito_identity_providers {
|
|
283
|
+
client_id = aws_cognito_user_pool_client.web_client.id
|
|
284
|
+
provider_name = aws_cognito_user_pool.user_pool.endpoint
|
|
285
|
+
server_side_token_check = true
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
tags = {
|
|
289
|
+
Name = "IdentityPool-${random_id.unique_suffix.hex}"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
# IAM roles for identity pool
|
|
294
|
+
resource "aws_iam_role" "authenticated_role" {
|
|
295
|
+
name = "cognito-authenticated-role-${random_id.unique_suffix.hex}"
|
|
296
|
+
|
|
297
|
+
assume_role_policy = jsonencode({
|
|
298
|
+
Version = "2012-10-17"
|
|
299
|
+
Statement = [
|
|
300
|
+
{
|
|
301
|
+
Effect = "Allow"
|
|
302
|
+
Principal = {
|
|
303
|
+
Federated = "cognito-identity.amazonaws.com"
|
|
304
|
+
}
|
|
305
|
+
Action = "sts:AssumeRoleWithWebIdentity"
|
|
306
|
+
Condition = {
|
|
307
|
+
StringEquals = {
|
|
308
|
+
"cognito-identity.amazonaws.com:aud" = aws_cognito_identity_pool.identity_pool.id
|
|
309
|
+
}
|
|
310
|
+
"ForAnyValue:StringLike" = {
|
|
311
|
+
"cognito-identity.amazonaws.com:amr" = "authenticated"
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
]
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
resource "aws_iam_role" "unauthenticated_role" {
|
|
320
|
+
name = "cognito-unauthenticated-role-${random_id.unique_suffix.hex}"
|
|
321
|
+
|
|
322
|
+
assume_role_policy = jsonencode({
|
|
323
|
+
Version = "2012-10-17"
|
|
324
|
+
Statement = [
|
|
325
|
+
{
|
|
326
|
+
Effect = "Allow"
|
|
327
|
+
Principal = {
|
|
328
|
+
Federated = "cognito-identity.amazonaws.com"
|
|
329
|
+
}
|
|
330
|
+
Action = "sts:AssumeRoleWithWebIdentity"
|
|
331
|
+
Condition = {
|
|
332
|
+
StringEquals = {
|
|
333
|
+
"cognito-identity.amazonaws.com:aud" = aws_cognito_identity_pool.identity_pool.id
|
|
334
|
+
}
|
|
335
|
+
"ForAnyValue:StringLike" = {
|
|
336
|
+
"cognito-identity.amazonaws.com:amr" = "unauthenticated"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
]
|
|
341
|
+
})
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
# Attach roles to identity pool
|
|
345
|
+
resource "aws_cognito_identity_pool_roles_attachment" "identity_pool_roles" {
|
|
346
|
+
identity_pool_id = aws_cognito_identity_pool.identity_pool.id
|
|
347
|
+
|
|
348
|
+
roles = {
|
|
349
|
+
"authenticated" = aws_iam_role.authenticated_role.arn
|
|
350
|
+
"unauthenticated" = aws_iam_role.unauthenticated_role.arn
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
# Managed Login Branding - temporarily commented out to resolve state conflicts
|
|
355
|
+
# Will be re-enabled after User Pool Client OAuth configuration is fixed
|
|
356
|
+
resource "aws_cognito_managed_login_branding" "managed_login_branding" {
|
|
357
|
+
user_pool_id = aws_cognito_user_pool.user_pool.id
|
|
358
|
+
client_id = aws_cognito_user_pool_client.web_client.id
|
|
359
|
+
use_cognito_provided_values = true
|
|
360
|
+
|
|
361
|
+
depends_on = [
|
|
362
|
+
aws_cognito_user_pool.user_pool,
|
|
363
|
+
aws_cognito_user_pool_client.web_client,
|
|
364
|
+
aws_cognito_user_pool_domain.user_pool_domain # commented out with domain
|
|
365
|
+
]
|
|
366
|
+
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
# Always add cognito props to runtime config
|
|
370
|
+
module "add_cognito_to_runtime_config" {
|
|
371
|
+
source = "../../runtime-config/entry"
|
|
372
|
+
|
|
373
|
+
key_path = "cognitoProps"
|
|
374
|
+
value = {
|
|
375
|
+
region = data.aws_region.current.name
|
|
376
|
+
identityPoolId = aws_cognito_identity_pool.identity_pool.id
|
|
377
|
+
userPoolId = aws_cognito_user_pool.user_pool.id
|
|
378
|
+
userPoolWebClientId = aws_cognito_user_pool_client.web_client.id
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
# Outputs
|
|
383
|
+
output "region" {
|
|
384
|
+
description = "AWS region"
|
|
385
|
+
value = data.aws_region.current.name
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
output "user_pool_id" {
|
|
389
|
+
description = "ID of the Cognito User Pool"
|
|
390
|
+
value = aws_cognito_user_pool.user_pool.id
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
output "user_pool_arn" {
|
|
394
|
+
description = "ARN of the Cognito User Pool"
|
|
395
|
+
value = aws_cognito_user_pool.user_pool.arn
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
output "user_pool_client_id" {
|
|
399
|
+
description = "ID of the Cognito User Pool Client"
|
|
400
|
+
value = aws_cognito_user_pool_client.web_client.id
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
output "identity_pool_id" {
|
|
404
|
+
description = "ID of the Cognito Identity Pool"
|
|
405
|
+
value = aws_cognito_identity_pool.identity_pool.id
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
output "authenticated_role_name" {
|
|
409
|
+
description = "Name of the authenticated IAM role"
|
|
410
|
+
value = aws_iam_role.authenticated_role.name
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
output "authenticated_role_arn" {
|
|
414
|
+
description = "ARN of the authenticated IAM role"
|
|
415
|
+
value = aws_iam_role.authenticated_role.arn
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
output "user_pool_domain" {
|
|
419
|
+
description = "Domain of the Cognito User Pool"
|
|
420
|
+
value = aws_cognito_user_pool_domain.user_pool_domain.domain
|
|
421
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module "identity" {
|
|
2
|
+
source = "./identity"
|
|
3
|
+
|
|
4
|
+
user_pool_domain_prefix = "<%= cognitoDomain %>"
|
|
5
|
+
allow_signup = <%= allowSignup %>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
# Outputs
|
|
9
|
+
output "region" {
|
|
10
|
+
description = "AWS region"
|
|
11
|
+
value = module.identity.region
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
output "user_pool_id" {
|
|
15
|
+
description = "ID of the Cognito User Pool"
|
|
16
|
+
value = module.identity.user_pool_id
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
output "user_pool_arn" {
|
|
20
|
+
description = "ARN of the Cognito User Pool"
|
|
21
|
+
value = module.identity.user_pool_arn
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
output "user_pool_client_id" {
|
|
25
|
+
description = "ID of the Cognito User Pool Client"
|
|
26
|
+
value = module.identity.user_pool_client_id
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
output "identity_pool_id" {
|
|
30
|
+
description = "ID of the Cognito Identity Pool"
|
|
31
|
+
value = module.identity.identity_pool_id
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
output "authenticated_role_name" {
|
|
35
|
+
description = "Name of the authenticated IAM role"
|
|
36
|
+
value = module.identity.authenticated_role_name
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
output "authenticated_role_arn" {
|
|
40
|
+
description = "ARN of the authenticated IAM role"
|
|
41
|
+
value = module.identity.authenticated_role_arn
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
output "user_pool_domain" {
|
|
45
|
+
description = "Domain of the Cognito User Pool"
|
|
46
|
+
value = module.identity.user_pool_domain
|
|
47
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { Tree } from '@nx/devkit';
|
|
6
|
+
import { IacProvider } from '../iac';
|
|
7
|
+
export interface AddIdentityInfraOptions {
|
|
8
|
+
cognitoDomain: string;
|
|
9
|
+
allowSignup: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Add infrastructure for a static website
|
|
13
|
+
*/
|
|
14
|
+
export declare const addIdentityInfra: (tree: Tree, options: AddIdentityInfraOptions & {
|
|
15
|
+
iacProvider: IacProvider;
|
|
16
|
+
}) => void;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addIdentityInfra = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
6
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
const devkit_1 = require("@nx/devkit");
|
|
9
|
+
const shared_constructs_constants_1 = require("../shared-constructs-constants");
|
|
10
|
+
const ast_1 = require("../ast");
|
|
11
|
+
/**
|
|
12
|
+
* Add infrastructure for a static website
|
|
13
|
+
*/
|
|
14
|
+
const addIdentityInfra = (tree, options) => {
|
|
15
|
+
if (options.iacProvider === 'CDK') {
|
|
16
|
+
addIdentityCdkConstructs(tree, options);
|
|
17
|
+
}
|
|
18
|
+
else if (options.iacProvider === 'Terraform') {
|
|
19
|
+
addIdentityTerraformModules(tree, options);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
throw new Error(`Unsupported iacProvider ${options.iacProvider}`);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
exports.addIdentityInfra = addIdentityInfra;
|
|
26
|
+
const addIdentityCdkConstructs = (tree, options) => {
|
|
27
|
+
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files', 'cdk', 'core'), (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_CONSTRUCTS_DIR, 'src', 'core'), options, {
|
|
28
|
+
overwriteStrategy: devkit_1.OverwriteStrategy.KeepExisting,
|
|
29
|
+
});
|
|
30
|
+
(0, ast_1.addStarExport)(tree, (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_CONSTRUCTS_DIR, 'src', 'core', 'index.ts'), './user-identity.js');
|
|
31
|
+
};
|
|
32
|
+
const addIdentityTerraformModules = (tree, options) => {
|
|
33
|
+
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files', 'terraform', 'core'), (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_TERRAFORM_DIR, 'src', 'core'), options, {
|
|
34
|
+
overwriteStrategy: devkit_1.OverwriteStrategy.KeepExisting,
|
|
35
|
+
});
|
|
36
|
+
// Update the static website module to add the callback url
|
|
37
|
+
const staticWebsiteModule = tree.read((0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_TERRAFORM_DIR, 'src', 'core', 'static-website', 'static-website.tf'), 'utf-8');
|
|
38
|
+
if (staticWebsiteModule &&
|
|
39
|
+
!staticWebsiteModule.includes('source = "../user-identity/add-callback-url"')) {
|
|
40
|
+
// Find the aws_cloudfront_distribution.website resource and add the callback URL module after it
|
|
41
|
+
const callbackUrlModule = `
|
|
42
|
+
|
|
43
|
+
# Add CloudFront domain to user pool client callback URLs
|
|
44
|
+
module "add_callback_url" {
|
|
45
|
+
source = "../user-identity/add-callback-url"
|
|
46
|
+
|
|
47
|
+
callback_url = "https://\${aws_cloudfront_distribution.website.domain_name}"
|
|
48
|
+
|
|
49
|
+
depends_on = [aws_cloudfront_distribution.website]
|
|
50
|
+
}`;
|
|
51
|
+
// Find the CloudFront distribution resource using proper brace counting
|
|
52
|
+
// This handles deeply nested structures correctly
|
|
53
|
+
const resourceStartPattern = /resource\s+"aws_cloudfront_distribution"\s+"website"\s*\{/;
|
|
54
|
+
const resourceStartMatch = staticWebsiteModule.match(resourceStartPattern);
|
|
55
|
+
if (resourceStartMatch) {
|
|
56
|
+
const startIndex = resourceStartMatch.index + resourceStartMatch[0].length - 1; // Position at opening brace
|
|
57
|
+
let braceCount = 0;
|
|
58
|
+
let insertionPoint = -1;
|
|
59
|
+
// Count braces to find the end of the resource block
|
|
60
|
+
for (let i = startIndex; i < staticWebsiteModule.length; i++) {
|
|
61
|
+
const char = staticWebsiteModule[i];
|
|
62
|
+
if (char === '{') {
|
|
63
|
+
braceCount++;
|
|
64
|
+
}
|
|
65
|
+
else if (char === '}') {
|
|
66
|
+
braceCount--;
|
|
67
|
+
if (braceCount === 0) {
|
|
68
|
+
// Found the closing brace of the CloudFront resource
|
|
69
|
+
insertionPoint = i + 1;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (insertionPoint !== -1) {
|
|
75
|
+
// Insert the callback URL module right after the CloudFront distribution
|
|
76
|
+
const beforeInsertion = staticWebsiteModule.substring(0, insertionPoint);
|
|
77
|
+
const afterInsertion = staticWebsiteModule.substring(insertionPoint);
|
|
78
|
+
const updatedContent = beforeInsertion + callbackUrlModule + afterInsertion;
|
|
79
|
+
tree.write((0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_TERRAFORM_DIR, 'src', 'core', 'static-website', 'static-website.tf'), updatedContent);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=identity-constructs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity-constructs.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/utils/identity-constructs/identity-constructs.ts"],"names":[],"mappings":"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"}
|