diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 960c7c8..d50b09a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -144,6 +144,7 @@ jobs: with: working_directory: infra/ soft_fail: true + github_token: ${{ secrets.GITHUB_TOKEN }} sbom: name: SBOM — Syft diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index 72d2bad..24e36d5 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -144,6 +144,7 @@ jobs: with: working_directory: infra/ soft_fail: true + github_token: ${{ secrets.GITHUB_TOKEN }} sbom: name: SBOM — Syft @@ -222,9 +223,7 @@ jobs: env: TF_VAR_environment: ${{ env.ENVIRONMENT }} TF_VAR_unsplash_access_key: ${{ secrets.UNSPLASH_ACCESS_KEY }} - TF_VAR_twilio_account_sid: ${{ secrets.TWILIO_ACCOUNT_SID }} - TF_VAR_twilio_auth_token: ${{ secrets.TWILIO_AUTH_TOKEN }} - TF_VAR_twilio_from_number: ${{ secrets.TWILIO_FROM_NUMBER }} + TF_VAR_sns_from_number: ${{ secrets.SNS_FROM_NUMBER }} run: | terraform apply -auto-approve \ -target=aws_ecr_repository.lambda \ @@ -251,18 +250,14 @@ jobs: env: TF_VAR_environment: ${{ env.ENVIRONMENT }} TF_VAR_unsplash_access_key: ${{ secrets.UNSPLASH_ACCESS_KEY }} - TF_VAR_twilio_account_sid: ${{ secrets.TWILIO_ACCOUNT_SID }} - TF_VAR_twilio_auth_token: ${{ secrets.TWILIO_AUTH_TOKEN }} - TF_VAR_twilio_from_number: ${{ secrets.TWILIO_FROM_NUMBER }} + TF_VAR_sns_from_number: ${{ secrets.SNS_FROM_NUMBER }} run: terraform apply -auto-approve -target=aws_cloudfront_distribution.main - name: Terraform Apply working-directory: infra env: TF_VAR_environment: ${{ env.ENVIRONMENT }} TF_VAR_unsplash_access_key: ${{ secrets.UNSPLASH_ACCESS_KEY }} - TF_VAR_twilio_account_sid: ${{ secrets.TWILIO_ACCOUNT_SID }} - TF_VAR_twilio_auth_token: ${{ secrets.TWILIO_AUTH_TOKEN }} - TF_VAR_twilio_from_number: ${{ secrets.TWILIO_FROM_NUMBER }} + TF_VAR_sns_from_number: ${{ secrets.SNS_FROM_NUMBER }} run: terraform apply -auto-approve - name: Read outputs id: tf-out diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index c473e21..7aef87a 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -67,9 +67,7 @@ jobs: env: TF_VAR_environment: ${{ env.ENVIRONMENT }} TF_VAR_unsplash_access_key: ${{ secrets.UNSPLASH_ACCESS_KEY }} - TF_VAR_twilio_account_sid: ${{ secrets.TWILIO_ACCOUNT_SID }} - TF_VAR_twilio_auth_token: ${{ secrets.TWILIO_AUTH_TOKEN }} - TF_VAR_twilio_from_number: ${{ secrets.TWILIO_FROM_NUMBER }} + TF_VAR_sns_from_number: ${{ secrets.SNS_FROM_NUMBER }} run: | terraform apply -auto-approve \ -target=aws_ecr_repository.lambda \ @@ -96,18 +94,14 @@ jobs: env: TF_VAR_environment: ${{ env.ENVIRONMENT }} TF_VAR_unsplash_access_key: ${{ secrets.UNSPLASH_ACCESS_KEY }} - TF_VAR_twilio_account_sid: ${{ secrets.TWILIO_ACCOUNT_SID }} - TF_VAR_twilio_auth_token: ${{ secrets.TWILIO_AUTH_TOKEN }} - TF_VAR_twilio_from_number: ${{ secrets.TWILIO_FROM_NUMBER }} + TF_VAR_sns_from_number: ${{ secrets.SNS_FROM_NUMBER }} run: terraform apply -auto-approve -target=aws_cloudfront_distribution.main - name: Terraform Apply working-directory: infra env: TF_VAR_environment: ${{ env.ENVIRONMENT }} TF_VAR_unsplash_access_key: ${{ secrets.UNSPLASH_ACCESS_KEY }} - TF_VAR_twilio_account_sid: ${{ secrets.TWILIO_ACCOUNT_SID }} - TF_VAR_twilio_auth_token: ${{ secrets.TWILIO_AUTH_TOKEN }} - TF_VAR_twilio_from_number: ${{ secrets.TWILIO_FROM_NUMBER }} + TF_VAR_sns_from_number: ${{ secrets.SNS_FROM_NUMBER }} run: terraform apply -auto-approve - name: Read outputs id: tf-out diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml index 212da17..1ee0623 100644 --- a/.github/workflows/deploy-qa.yml +++ b/.github/workflows/deploy-qa.yml @@ -127,6 +127,7 @@ jobs: with: working_directory: infra/ soft_fail: true + github_token: ${{ secrets.GITHUB_TOKEN }} sbom: name: SBOM — Syft @@ -202,9 +203,7 @@ jobs: env: TF_VAR_environment: ${{ env.ENVIRONMENT }} TF_VAR_unsplash_access_key: ${{ secrets.UNSPLASH_ACCESS_KEY }} - TF_VAR_twilio_account_sid: ${{ secrets.TWILIO_ACCOUNT_SID }} - TF_VAR_twilio_auth_token: ${{ secrets.TWILIO_AUTH_TOKEN }} - TF_VAR_twilio_from_number: ${{ secrets.TWILIO_FROM_NUMBER }} + TF_VAR_sns_from_number: ${{ secrets.SNS_FROM_NUMBER }} run: | terraform apply -auto-approve \ -target=aws_ecr_repository.lambda \ @@ -231,18 +230,14 @@ jobs: env: TF_VAR_environment: ${{ env.ENVIRONMENT }} TF_VAR_unsplash_access_key: ${{ secrets.UNSPLASH_ACCESS_KEY }} - TF_VAR_twilio_account_sid: ${{ secrets.TWILIO_ACCOUNT_SID }} - TF_VAR_twilio_auth_token: ${{ secrets.TWILIO_AUTH_TOKEN }} - TF_VAR_twilio_from_number: ${{ secrets.TWILIO_FROM_NUMBER }} + TF_VAR_sns_from_number: ${{ secrets.SNS_FROM_NUMBER }} run: terraform apply -auto-approve -target=aws_cloudfront_distribution.main - name: Terraform Apply working-directory: infra env: TF_VAR_environment: ${{ env.ENVIRONMENT }} TF_VAR_unsplash_access_key: ${{ secrets.UNSPLASH_ACCESS_KEY }} - TF_VAR_twilio_account_sid: ${{ secrets.TWILIO_ACCOUNT_SID }} - TF_VAR_twilio_auth_token: ${{ secrets.TWILIO_AUTH_TOKEN }} - TF_VAR_twilio_from_number: ${{ secrets.TWILIO_FROM_NUMBER }} + TF_VAR_sns_from_number: ${{ secrets.SNS_FROM_NUMBER }} run: terraform apply -auto-approve - name: Read outputs id: tf-out diff --git a/backend/package-lock.json b/backend/package-lock.json index 6abb2ce..71b7d72 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,6 +13,7 @@ "@aws-sdk/client-cognito-identity-provider": "^3.1051.0", "@aws-sdk/client-dynamodb": "^3.1051.0", "@aws-sdk/client-kms": "^3.1056.0", + "@aws-sdk/client-pinpoint-sms-voice-v2": "^3.1073.0", "@aws-sdk/client-ssm": "^3.1056.0", "@aws-sdk/lib-dynamodb": "^3.1051.0", "@pothos/core": "^4.12.0", @@ -416,6 +417,27 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/client-pinpoint-sms-voice-v2": { + "version": "3.1073.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-pinpoint-sms-voice-v2/-/client-pinpoint-sms-voice-v2-3.1073.0.tgz", + "integrity": "sha512-Tnx2qODlLp+qoKFEKrDFvMurjRmaKu0fDRqTDB4vcV6+aXUNXaqPQSvBjmuX6LTNo47ZzOgkY7gFRVnRRBur9Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.22", + "@aws-sdk/credential-provider-node": "^3.972.57", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/fetch-http-handler": "^5.4.6", + "@smithy/node-http-handler": "^4.7.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/client-ssm": { "version": "3.1056.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-ssm/-/client-ssm-3.1056.0.tgz", @@ -438,17 +460,17 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.974.15", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.15.tgz", - "integrity": "sha512-UpA0rTGW/tHGITcCqHisbuuEPraYg9GG+mWmXjY5+RxZBMLGe6aL9oe0ix50LztwAcPIkGZLH0yWdMIkCM10hw==", + "version": "3.974.22", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.22.tgz", + "integrity": "sha512-YofH63shc6YRdXjz80BJkpJW+Bkn0Cuu2dn4Rv7s9G2Idt58tgtzQEWxrR2xVljlVfIBeUjPuULnSVYLke3sUQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.9", - "@aws-sdk/xml-builder": "^3.972.26", + "@aws-sdk/types": "^3.973.13", + "@aws-sdk/xml-builder": "^3.972.30", "@aws/lambda-invoke-store": "^0.2.2", - "@smithy/core": "^3.24.5", - "@smithy/signature-v4": "^5.4.5", - "@smithy/types": "^4.14.2", + "@smithy/core": "^3.24.6", + "@smithy/signature-v4": "^5.4.6", + "@smithy/types": "^4.14.3", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -457,15 +479,15 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.41.tgz", - "integrity": "sha512-n1EbJ98yvPWWdHZZv8bRBMqqDQJrtgtxyJ4xLy2Uqrh25BCOZQ7nnS1CsFXvuH8r0b0KVHDZEGEH5FxmEMP8jg==", + "version": "3.972.48", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.48.tgz", + "integrity": "sha512-h6FEC95fbexUd6zxm4PdgS82bTcI2PRtUb2ZwMipb/Xr8bPwtf0G8rBo2jp7NA24Mbx2JA8/WingiYpA9RCCyw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.15", - "@aws-sdk/types": "^3.973.9", - "@smithy/core": "^3.24.5", - "@smithy/types": "^4.14.2", + "@aws-sdk/core": "^3.974.22", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", "tslib": "^2.6.2" }, "engines": { @@ -473,17 +495,17 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.43", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.43.tgz", - "integrity": "sha512-TT76RN1NkI9WoyZqCNxOw6/WBMF7pYOTJcXbMokNFU+euSG40Kaf/t/FhDACVZWP+43wEM6ZynIPIkzS1wR1iA==", + "version": "3.972.50", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.50.tgz", + "integrity": "sha512-lJO3OLpjvz5m/RSBQmsG/CEUGsvCy5ruxKwPQaOCqxqCMuyYT2BZwQUTDZVVwqQ9LrZKuK24JSa6r31hL/tvkg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.15", - "@aws-sdk/types": "^3.973.9", - "@smithy/core": "^3.24.5", - "@smithy/fetch-http-handler": "^5.4.5", - "@smithy/node-http-handler": "^4.7.5", - "@smithy/types": "^4.14.2", + "@aws-sdk/core": "^3.974.22", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/fetch-http-handler": "^5.4.6", + "@smithy/node-http-handler": "^4.7.6", + "@smithy/types": "^4.14.3", "tslib": "^2.6.2" }, "engines": { @@ -491,23 +513,23 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.45", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.45.tgz", - "integrity": "sha512-sJe5ZWibO4s7RWjFQ8Zol76KxoJcIYyEZH1/wxQSBMSIAAxzaJ8cS/ITAaIHWUQvDKQdt18+cJAHKWB7n1Jmrg==", + "version": "3.972.55", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.55.tgz", + "integrity": "sha512-TBoF4buBGYhXjdZAryayY2TrkQj2B2KfE/msG4V53XCt+w0EhEwM2JRjx8p2grJ2C6gtH5++SAwEvGMRdi0yyw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.15", - "@aws-sdk/credential-provider-env": "^3.972.41", - "@aws-sdk/credential-provider-http": "^3.972.43", - "@aws-sdk/credential-provider-login": "^3.972.45", - "@aws-sdk/credential-provider-process": "^3.972.41", - "@aws-sdk/credential-provider-sso": "^3.972.45", - "@aws-sdk/credential-provider-web-identity": "^3.972.45", - "@aws-sdk/nested-clients": "^3.997.13", - "@aws-sdk/types": "^3.973.9", - "@smithy/core": "^3.24.5", - "@smithy/credential-provider-imds": "^4.3.5", - "@smithy/types": "^4.14.2", + "@aws-sdk/core": "^3.974.22", + "@aws-sdk/credential-provider-env": "^3.972.48", + "@aws-sdk/credential-provider-http": "^3.972.50", + "@aws-sdk/credential-provider-login": "^3.972.54", + "@aws-sdk/credential-provider-process": "^3.972.48", + "@aws-sdk/credential-provider-sso": "^3.972.54", + "@aws-sdk/credential-provider-web-identity": "^3.972.54", + "@aws-sdk/nested-clients": "^3.997.22", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/credential-provider-imds": "^4.3.7", + "@smithy/types": "^4.14.3", "tslib": "^2.6.2" }, "engines": { @@ -515,16 +537,16 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.45", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.45.tgz", - "integrity": "sha512-MZQv4SNjByk1iOKmrqmzcUF/uCB05wjvEHyXKxmGQTUANTIVayX6HPUF0bzkWLvtnkH7sAn9kUCfkXbSpj9sDA==", + "version": "3.972.54", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.54.tgz", + "integrity": "sha512-hBWI3wZTdTGiuMfmPts6AWbAjFfRniOQnqx68tc2cQvRKWawFbN9wkLOVPWM1FAOyowZU73mC6Fi+rHSHNyLFw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.15", - "@aws-sdk/nested-clients": "^3.997.13", - "@aws-sdk/types": "^3.973.9", - "@smithy/core": "^3.24.5", - "@smithy/types": "^4.14.2", + "@aws-sdk/core": "^3.974.22", + "@aws-sdk/nested-clients": "^3.997.22", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", "tslib": "^2.6.2" }, "engines": { @@ -532,21 +554,21 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.46", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.46.tgz", - "integrity": "sha512-cS4w0jzDRb1jOlkiJS3y80OxddHzkky/MN9k3NYs5jganNKVLjF0lpvjlwS118oGMr3cdAfOlVdo8gLurTSE7w==", + "version": "3.972.57", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.57.tgz", + "integrity": "sha512-u6dClpzNdWf1HGWz4wwhdXi1wiOofCLniM9S4BQQGlLAN9TW7VB+ld5V533GdKrYMaFeBGFqKnj0JCYvynLqwQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.41", - "@aws-sdk/credential-provider-http": "^3.972.43", - "@aws-sdk/credential-provider-ini": "^3.972.45", - "@aws-sdk/credential-provider-process": "^3.972.41", - "@aws-sdk/credential-provider-sso": "^3.972.45", - "@aws-sdk/credential-provider-web-identity": "^3.972.45", - "@aws-sdk/types": "^3.973.9", - "@smithy/core": "^3.24.5", - "@smithy/credential-provider-imds": "^4.3.5", - "@smithy/types": "^4.14.2", + "@aws-sdk/credential-provider-env": "^3.972.48", + "@aws-sdk/credential-provider-http": "^3.972.50", + "@aws-sdk/credential-provider-ini": "^3.972.55", + "@aws-sdk/credential-provider-process": "^3.972.48", + "@aws-sdk/credential-provider-sso": "^3.972.54", + "@aws-sdk/credential-provider-web-identity": "^3.972.54", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/credential-provider-imds": "^4.3.7", + "@smithy/types": "^4.14.3", "tslib": "^2.6.2" }, "engines": { @@ -554,15 +576,15 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.41.tgz", - "integrity": "sha512-7I/n1zkysouLOWvkEhjNEP4vMnD2v4kzzr3/3QBdrripEpn7ap1/I5DF3Hou1SUqkKWo1f3oPGMyFAA1FAMvsQ==", + "version": "3.972.48", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.48.tgz", + "integrity": "sha512-w6VZwojPt12WnEkAUy6Nu4K6sWCbBmR7QX390b0nE6vRvkXbrYr9Lq9VySGkfjiMjpUA87op+J4EgvRmtWIDoQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.15", - "@aws-sdk/types": "^3.973.9", - "@smithy/core": "^3.24.5", - "@smithy/types": "^4.14.2", + "@aws-sdk/core": "^3.974.22", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", "tslib": "^2.6.2" }, "engines": { @@ -570,17 +592,17 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.45", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.45.tgz", - "integrity": "sha512-oHgbz/eFD8IKiksqDsz9ZMU4A59BpQq4QwJedBnGD80ZqYcHPPHZBwjBnxLVkB7iRVVHWpDclR8yWdD2PkQIUA==", + "version": "3.972.54", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.54.tgz", + "integrity": "sha512-23uZpIpF2SIFDCa1fcWa202tK4gGeyvX6GIIAjiB8WBsvsVRBMnJ/7dCxHzxf7eZT7GToJg837LDIBnZsl/VUg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.15", - "@aws-sdk/nested-clients": "^3.997.13", - "@aws-sdk/token-providers": "3.1056.0", - "@aws-sdk/types": "^3.973.9", - "@smithy/core": "^3.24.5", - "@smithy/types": "^4.14.2", + "@aws-sdk/core": "^3.974.22", + "@aws-sdk/nested-clients": "^3.997.22", + "@aws-sdk/token-providers": "3.1071.0", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", "tslib": "^2.6.2" }, "engines": { @@ -588,16 +610,16 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.45", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.45.tgz", - "integrity": "sha512-CDhzKdb2onv5bpnjn/acgdNmJOQthPDLsPizU7rZflsEcgMMp8Mlri+U5hdxf8ldvZJpvM3vLU6D56vfJm5AMQ==", + "version": "3.972.54", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.54.tgz", + "integrity": "sha512-0Iv5QttS6wcATlodYKgvQj6B9Db51rx7NU9fqu0PoLeS4BIgdYMc/QK4smwLwpm5RFrs02V/eLyEFp3FklvlNQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.15", - "@aws-sdk/nested-clients": "^3.997.13", - "@aws-sdk/types": "^3.973.9", - "@smithy/core": "^3.24.5", - "@smithy/types": "^4.14.2", + "@aws-sdk/core": "^3.974.22", + "@aws-sdk/nested-clients": "^3.997.22", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", "tslib": "^2.6.2" }, "engines": { @@ -668,20 +690,20 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.997.13", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.13.tgz", - "integrity": "sha512-2pA6eyb5nSo/ZD2cayhOTEMoGQYgspq0RI05GDLkzQ3ajZ6isS6waV6E92Am/hz4LIlLUTrbwPLurJ/fuiHvkg==", + "version": "3.997.22", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.22.tgz", + "integrity": "sha512-4IwtcYSxEIVw5hcp8ogq0CMbFNZFw7jJUetpfFUhFFeqsa1K8j2Ihg2hnxLyOp3stMZnXda6VzOmPi1AFZQXcg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.15", - "@aws-sdk/signature-v4-multi-region": "^3.996.30", - "@aws-sdk/types": "^3.973.9", - "@smithy/core": "^3.24.5", - "@smithy/fetch-http-handler": "^5.4.5", - "@smithy/node-http-handler": "^4.7.5", - "@smithy/types": "^4.14.2", + "@aws-sdk/core": "^3.974.22", + "@aws-sdk/signature-v4-multi-region": "^3.996.35", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/fetch-http-handler": "^5.4.6", + "@smithy/node-http-handler": "^4.7.6", + "@smithy/types": "^4.14.3", "tslib": "^2.6.2" }, "engines": { @@ -689,14 +711,14 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.30", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.30.tgz", - "integrity": "sha512-HULDLMVzkmTSEv6//7kx2kRevp/VYUpm8hJNNFbmhxDn0fUiGTxVcM9yg31TukvTq8nyOBDUN2gH0o5IRbKjdw==", + "version": "3.996.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.35.tgz", + "integrity": "sha512-6L/VWs+Wch2stHemCGTmUNqKLMzURxQDK5boNG3Jn3kAOp71meDUuS5sbObpEvFxHDq0uWeSLFDNSYsjNt+Dlg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.9", - "@smithy/signature-v4": "^5.4.5", - "@smithy/types": "^4.14.2", + "@aws-sdk/types": "^3.973.13", + "@smithy/signature-v4": "^5.4.6", + "@smithy/types": "^4.14.3", "tslib": "^2.6.2" }, "engines": { @@ -704,16 +726,16 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.1056.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1056.0.tgz", - "integrity": "sha512-81duvlltQlsfn5K+o8zILcystBRdbT1G2JJYVCML5NZHBz4CL/zf+sAemCtBh/uh6RQUMyInGeZLQ7/8igZhbA==", + "version": "3.1071.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1071.0.tgz", + "integrity": "sha512-4LDW2Qob6LoLFuqYSYZq2AyTE9koSE9+i+n5UZcm10GpmQOK0zRD9L4uYlzItiTKksIWgC/qMFChAi3RvKYtMg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.974.15", - "@aws-sdk/nested-clients": "^3.997.13", - "@aws-sdk/types": "^3.973.9", - "@smithy/core": "^3.24.5", - "@smithy/types": "^4.14.2", + "@aws-sdk/core": "^3.974.22", + "@aws-sdk/nested-clients": "^3.997.22", + "@aws-sdk/types": "^3.973.13", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", "tslib": "^2.6.2" }, "engines": { @@ -721,12 +743,12 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.973.9", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.9.tgz", - "integrity": "sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg==", + "version": "3.973.13", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.13.tgz", + "integrity": "sha512-pEHZqRkAlHfnfAU9tK+WpKv/gBNjGJrHMgA3A0iYRGyswBS2t0pfez+lWlwktb3Bqa0ovh7w/QJTFwp3fDxLNg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.14.2", + "@smithy/types": "^4.14.3", "tslib": "^2.6.2" }, "engines": { @@ -761,12 +783,12 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.26", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.26.tgz", - "integrity": "sha512-cDbrqvDS73whl6YAPSPq0U6whzG6UWI9PuWh0wrUuGoZexhWEqhdunbukV7iBoaWnFV1AODutM5hOD6rtn439g==", + "version": "3.972.30", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.30.tgz", + "integrity": "sha512-StElZPEoBquWwNqw1AcfpzEyZqJvFxouG+mpDNYlcH6ZOrqd2CuIryv+8LV8gNHZUOyKyJF3Dq9vxaXEmDR9TQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.14.2", + "@smithy/types": "^4.14.3", "fast-xml-parser": "5.7.3", "tslib": "^2.6.2" }, @@ -2755,9 +2777,9 @@ } }, "node_modules/@nodable/entities": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.1.tgz", - "integrity": "sha512-Pig3HxDIoMgjdEH8OCf/dkcTmLFjJRjWuq8jSnklu284/TKOPibSRERmOykiwmyXTtv61mP+44f3GMx0tLAyjg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", "funding": [ { "type": "github", @@ -2833,13 +2855,13 @@ } }, "node_modules/@smithy/core": { - "version": "3.24.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.5.tgz", - "integrity": "sha512-Kt8phUg45M15EjhYAbZ+fFikYneijLu9Liugz8ZsYz2i8j0hzGv27LWKpEHYRfvj+LyCOSijpcR/2i8RouV+cA==", + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.25.1.tgz", + "integrity": "sha512-zpDbpXBCBsxfLtG2GEUyfgvHvSFrw5CwDZSNzL0v52gx/c3oPlPbm+7W7num8xs6vyiUBn+bvYPHcQDOXZynCQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.14.2", + "@smithy/types": "^4.15.0", "tslib": "^2.6.2" }, "engines": { @@ -2847,13 +2869,13 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.5.tgz", - "integrity": "sha512-yiF8xHpdkaTfzLVqFzsP6WvNghEK+qZzLYWFD13L2SsFhbXwBGlxdocKF95qjr7s5lE5NRage+EJFK4mAsx88Q==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.1.tgz", + "integrity": "sha512-TSAF5NHgxEsllbErYWbK8aLnl5L601NGc5VYJlSPsKnf3YlkhdoBN+geGcaU00oiw2OK3QO5LA3QNXiiWhCidQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.24.5", - "@smithy/types": "^4.14.2", + "@smithy/core": "^3.25.1", + "@smithy/types": "^4.15.0", "tslib": "^2.6.2" }, "engines": { @@ -2861,13 +2883,13 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.5.tgz", - "integrity": "sha512-SK3VMeH0fibgdTg2QeB+O4p7Yy/2E5HBOHJeC58FshkDdeuX8lOgO7PfjYfLyPLP1ch55j91cQqKBzDS0mRjSQ==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.5.1.tgz", + "integrity": "sha512-96JrD1q71anokymx9Iblb+zKmNQYNstlV/25A9ZYIJ2A0rp1r7/GZAIm0bDWSmVvz3DpNOCZuabzsiL+w0UHhw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.24.5", - "@smithy/types": "^4.14.2", + "@smithy/core": "^3.25.1", + "@smithy/types": "^4.15.0", "tslib": "^2.6.2" }, "engines": { @@ -2887,13 +2909,13 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.5.tgz", - "integrity": "sha512-3dA9TQ+ybRSZ/m0wnbZhiBy4Dezjgq1Ib/ZZrYTpJDBgpoLLU/SDzZc/g0x0MNAdOJe1wPcM+x2PBRmoOur+Sw==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.8.1.tgz", + "integrity": "sha512-emtXvoky671puri18ETf64AFIQUGIEA093F2drXpBgB0OGnBLjcwNR3CA2mYu62IAqNsS56xa5lnTxAgPq7cjw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.24.5", - "@smithy/types": "^4.14.2", + "@smithy/core": "^3.25.1", + "@smithy/types": "^4.15.0", "tslib": "^2.6.2" }, "engines": { @@ -2901,13 +2923,13 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.5.tgz", - "integrity": "sha512-QBJKWGqIknH0dc9LWpfH1mkdokAx6iXYN3UcQ3eY6uIEyScuoQAhfl94ge7ozUy9WgFUdE8xsvwBjaYBbWmPNA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.5.1.tgz", + "integrity": "sha512-X9rVls3En0z3NtrmguTmpRM0/NqtWUxBjal6fcAkwtsub+gOdLZ6kD+V7xhUgFMGdG14bHbZ7M5QjaRI1+DatQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.24.5", - "@smithy/types": "^4.14.2", + "@smithy/core": "^3.25.1", + "@smithy/types": "^4.15.0", "tslib": "^2.6.2" }, "engines": { @@ -2915,9 +2937,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", - "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.15.0.tgz", + "integrity": "sha512-Z5TAOxygoFvybJV3igo5SloFflSokHx2hu1eFA+DxDTcn+FtKxUSui+rbTRG1pAafMA888Z3MVvCWUuvCrTXjg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3945,6 +3967,18 @@ "node": ">= 8" } }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -6590,9 +6624,9 @@ } }, "node_modules/path-expression-matcher": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", - "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.0.tgz", + "integrity": "sha512-e5y7RCLHKjemsgQ4eqGJtPyr10ILz25HO7flzxhTV8bgvd5yHx98DGtCAtbVW9f2TqnYI/gEVZd+vz7snrdPTw==", "funding": [ { "type": "github", @@ -7215,16 +7249,19 @@ } }, "node_modules/strnum": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", - "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", + "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "MIT" + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } }, "node_modules/supports-color": { "version": "7.2.0", diff --git a/backend/package.json b/backend/package.json index 668cb21..88d24a2 100644 --- a/backend/package.json +++ b/backend/package.json @@ -22,6 +22,7 @@ "@aws-sdk/client-cognito-identity-provider": "^3.1051.0", "@aws-sdk/client-dynamodb": "^3.1051.0", "@aws-sdk/client-kms": "^3.1056.0", + "@aws-sdk/client-pinpoint-sms-voice-v2": "^3.1073.0", "@aws-sdk/client-ssm": "^3.1056.0", "@aws-sdk/lib-dynamodb": "^3.1051.0", "@pothos/core": "^4.12.0", diff --git a/backend/src/handler.ts b/backend/src/handler.ts index 97c8dbb..560fc59 100644 --- a/backend/src/handler.ts +++ b/backend/src/handler.ts @@ -3,7 +3,7 @@ * * Adapts a Lambda Function URL event (API Gateway payload format 2.0) into a Fetch * `Request`, runs it through GraphQL Yoga, and converts the `Response` back. - * Also routes Cognito CustomSMSSender_* trigger events to the Twilio SMS sender. + * Also routes Cognito CustomSMSSender_* trigger events to the Pinpoint SMS sender. */ import type { LambdaFunctionURLEvent, LambdaFunctionURLResult } from 'aws-lambda'; import { yoga } from './yoga'; diff --git a/backend/src/smsSender.ts b/backend/src/smsSender.ts index 0ce52a9..e94bb97 100644 --- a/backend/src/smsSender.ts +++ b/backend/src/smsSender.ts @@ -3,18 +3,22 @@ * * Cognito encrypts the OTP code using the AWS Encryption SDK (not raw KMS) * before passing it to this Lambda. We decrypt it with the same SDK, then - * deliver via Twilio's REST API — bypassing the AWS SNS origination-number - * registration requirement in the US. + * deliver via Pinpoint SMS Voice v2 using the dedicated toll-free origination + * number — giving us full control over the origination identity and message. * * Trigger sources handled: any CustomSMSSender_* event (verification codes, * MFA codes, forgot-password codes, admin-created password codes). */ import { buildClient, CommitmentPolicy, KmsKeyringNode } from '@aws-crypto/client-node'; import { SSMClient, GetParameterCommand } from '@aws-sdk/client-ssm'; -import * as https from 'https'; +import { + PinpointSMSVoiceV2Client, + SendTextMessageCommand, +} from '@aws-sdk/client-pinpoint-sms-voice-v2'; const { decrypt } = buildClient(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT); const ssm = new SSMClient({}); +const pinpoint = new PinpointSMSVoiceV2Client({}); export interface CognitoCustomSMSEvent { triggerSource: string; @@ -24,60 +28,28 @@ export interface CognitoCustomSMSEvent { }; } -let twilioCache: { accountSid: string; authToken: string; from: string } | null = null; +let originationNumberCache: string | null = null; -async function getTwilioConfig(): Promise<{ accountSid: string; authToken: string; from: string }> { - if (twilioCache) return twilioCache; +async function getOriginationNumber(): Promise { + if (originationNumberCache) return originationNumberCache; const env = process.env.ENVIRONMENT ?? 'dev'; - const [sidRes, tokenRes, fromRes] = await Promise.all([ - ssm.send( - new GetParameterCommand({ Name: `/bba/${env}/twilio_account_sid`, WithDecryption: true }), - ), - ssm.send( - new GetParameterCommand({ Name: `/bba/${env}/twilio_auth_token`, WithDecryption: true }), - ), - ssm.send( - new GetParameterCommand({ Name: `/bba/${env}/twilio_from_number`, WithDecryption: false }), - ), - ]); - twilioCache = { - accountSid: sidRes.Parameter?.Value ?? '', - authToken: tokenRes.Parameter?.Value ?? '', - from: fromRes.Parameter?.Value ?? '', - }; - return twilioCache; + const res = await ssm.send( + new GetParameterCommand({ Name: `/bba/${env}/sns_from_number`, WithDecryption: false }), + ); + originationNumberCache = res.Parameter?.Value ?? ''; + return originationNumberCache; } async function sendSMS(to: string, body: string): Promise { - const { accountSid, authToken, from } = await getTwilioConfig(); - const payload = new URLSearchParams({ To: to, From: from, Body: body }).toString(); - const auth = Buffer.from(`${accountSid}:${authToken}`).toString('base64'); - - await new Promise((resolve, reject) => { - const req = https.request( - { - hostname: 'api.twilio.com', - path: `/2010-04-01/Accounts/${accountSid}/Messages.json`, - method: 'POST', - headers: { - Authorization: `Basic ${auth}`, - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': Buffer.byteLength(payload), - }, - }, - (res) => { - if (res.statusCode !== undefined && res.statusCode >= 200 && res.statusCode < 300) { - resolve(); - } else { - reject(new Error(`Twilio responded with ${String(res.statusCode)}`)); - } - res.resume(); - }, - ); - req.on('error', reject); - req.write(payload); - req.end(); - }); + const originationIdentity = await getOriginationNumber(); + await pinpoint.send( + new SendTextMessageCommand({ + DestinationPhoneNumber: to, + OriginationIdentity: originationIdentity, + MessageBody: body, + MessageType: 'TRANSACTIONAL', + }), + ); } export async function handleCustomSMS(event: CognitoCustomSMSEvent): Promise { @@ -92,5 +64,6 @@ export async function handleCustomSMS(event: CognitoCustomSMSEvent): Promise { - name = dvo.resource_record_name - record = dvo.resource_record_value - type = dvo.resource_record_type - } - } : {} + for_each = local.is_prod ? toset([local.domain, "www.${local.domain}"]) : toset([]) allow_overwrite = true - name = each.value.name - records = [each.value.record] - ttl = 60 - type = each.value.type - zone_id = data.aws_route53_zone.main[0].zone_id + name = tolist([ + for dvo in aws_acm_certificate.main[0].domain_validation_options : + dvo.resource_record_name if dvo.domain_name == each.key + ])[0] + records = [tolist([ + for dvo in aws_acm_certificate.main[0].domain_validation_options : + dvo.resource_record_value if dvo.domain_name == each.key + ])[0]] + type = tolist([ + for dvo in aws_acm_certificate.main[0].domain_validation_options : + dvo.resource_record_type if dvo.domain_name == each.key + ])[0] + ttl = 60 + zone_id = data.aws_route53_zone.main[0].zone_id } resource "aws_acm_certificate_validation" "main" { @@ -255,3 +262,29 @@ resource "aws_route53_record" "www" { evaluate_target_health = false } } + +# --------------------------------------------------------------------------- +# Email (ImprovMX forwarding) — production only; domain-level records +# Forwards hello@buildbetteralgorithms.com to the owner's personal inbox. +# --------------------------------------------------------------------------- + +resource "aws_route53_record" "mx" { + count = local.is_prod ? 1 : 0 + zone_id = data.aws_route53_zone.main[0].zone_id + name = local.domain + type = "MX" + ttl = 300 + records = [ + "10 mx1.improvmx.com", + "20 mx2.improvmx.com", + ] +} + +resource "aws_route53_record" "spf" { + count = local.is_prod ? 1 : 0 + zone_id = data.aws_route53_zone.main[0].zone_id + name = local.domain + type = "TXT" + ttl = 300 + records = ["v=spf1 include:spf.improvmx.com ~all"] +} diff --git a/infra/secrets.tf b/infra/secrets.tf index 7d9b3e7..820ec2a 100644 --- a/infra/secrets.tf +++ b/infra/secrets.tf @@ -22,27 +22,11 @@ resource "aws_ssm_parameter" "cognito_user_pool_id" { tags = local.tags } -resource "aws_ssm_parameter" "twilio_account_sid" { - name = "/bba/${local.env}/twilio_account_sid" - description = "Twilio Account SID for custom SMS sender" - type = "SecureString" - value = var.twilio_account_sid - tags = local.tags -} - -resource "aws_ssm_parameter" "twilio_auth_token" { - name = "/bba/${local.env}/twilio_auth_token" - description = "Twilio Auth Token for custom SMS sender" - type = "SecureString" - value = var.twilio_auth_token - tags = local.tags -} - -resource "aws_ssm_parameter" "twilio_from_number" { - name = "/bba/${local.env}/twilio_from_number" - description = "Twilio origination phone number in E.164 format" +resource "aws_ssm_parameter" "sns_from_number" { + name = "/bba/${local.env}/sns_from_number" + description = "AWS SNS origination phone number in E.164 format for custom SMS sender" type = "String" - value = var.twilio_from_number + value = var.sns_from_number tags = local.tags } diff --git a/infra/variables.tf b/infra/variables.tf index 493fb49..1f182d9 100644 --- a/infra/variables.tf +++ b/infra/variables.tf @@ -14,20 +14,8 @@ variable "unsplash_access_key" { sensitive = true } -variable "twilio_account_sid" { - description = "Twilio Account SID. Pass via TF_VAR_twilio_account_sid in CI — never commit the value." - type = string - sensitive = true -} - -variable "twilio_auth_token" { - description = "Twilio Auth Token. Pass via TF_VAR_twilio_auth_token in CI — never commit the value." - type = string - sensitive = true -} - -variable "twilio_from_number" { - description = "Twilio origination phone number in E.164 format (e.g. +19049439891)." +variable "sns_from_number" { + description = "AWS SNS origination phone number in E.164 format (e.g. +19049439891). Pass via TF_VAR_sns_from_number in CI." type = string }