diff --git a/.env.example b/.env.example index 79539fe..eeea5d0 100644 --- a/.env.example +++ b/.env.example @@ -10,8 +10,8 @@ SMTP_USER=test_userdadad@example.com # Example SMTP user AWS_DEFAULT_REGION="us-east-1" -AWS_SECRET_KEY="some-secret-key" -AWS_ACCESS_KEY="some-access-key" +AWS_ACCESS_KEY_ID="some-access-key" +AWS_SECRET_ACCESS_KEY="some-secret-key" AWS_SES_ENDPOINT="http://localhost:3003/api/ses" AWS_SNS_ENDPOINT="http://localhost:3003/api/sns" diff --git a/.env.selfhost.example b/.env.selfhost.example index d294bd3..976d778 100644 --- a/.env.selfhost.example +++ b/.env.selfhost.example @@ -25,10 +25,12 @@ GITHUB_SECRET="" GOOGLE_CLIENT_ID="" GOOGLE_CLIENT_SECRET="" -# AWS details - required +# AWS details +# Provide static credentials OR rely on the AWS default credential chain +# (IAM role, ECS task role, instance profile, etc.) by omitting these vars. AWS_DEFAULT_REGION="us-east-1" -AWS_SECRET_KEY="" -AWS_ACCESS_KEY="" +AWS_ACCESS_KEY_ID="" +AWS_SECRET_ACCESS_KEY="" diff --git a/.github/workflows/test-web.yml b/.github/workflows/test-web.yml index d5139ed..c8e751e 100644 --- a/.github/workflows/test-web.yml +++ b/.github/workflows/test-web.yml @@ -19,8 +19,8 @@ jobs: NEXTAUTH_SECRET: test-secret DATABASE_URL: postgresql://usesend:password@127.0.0.1:5432/usesend_test REDIS_URL: redis://127.0.0.1:6379/15 - AWS_ACCESS_KEY: test-access-key - AWS_SECRET_KEY: test-secret-key + AWS_ACCESS_KEY_ID: test-access-key + AWS_SECRET_ACCESS_KEY: test-secret-key AWS_DEFAULT_REGION: us-east-1 NEXT_PUBLIC_IS_CLOUD: "true" API_RATE_LIMIT: "2" diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md index 71ad69c..b9754b3 100644 --- a/CONTRIBUTION.md +++ b/CONTRIBUTION.md @@ -82,8 +82,8 @@ GITHUB_SECRET=your_client_secret If you want to send real emails, add: ```env -AWS_ACCESS_KEY=your_access_key -AWS_SECRET_KEY=your_secret_key +AWS_ACCESS_KEY_ID=your_access_key +AWS_SECRET_ACCESS_KEY=your_secret_key ``` > You can skip this by using the `local-sen-sns` image for local-only email development. diff --git a/apps/docs/get-started/create-aws-credentials.mdx b/apps/docs/get-started/create-aws-credentials.mdx index 4823c10..4b4a0ed 100644 --- a/apps/docs/get-started/create-aws-credentials.mdx +++ b/apps/docs/get-started/create-aws-credentials.mdx @@ -28,8 +28,8 @@ description: Step by step guide to create AWS credentials to self-host useSend. Copy the access key ID and secret access key to your `.env` file. ```env - AWS_ACCESS_KEY= - AWS_SECRET_KEY= + AWS_ACCESS_KEY_ID= + AWS_SECRET_ACCESS_KEY= ``` ![create access key](/images/aws/key-6.png) diff --git a/apps/docs/get-started/local.mdx b/apps/docs/get-started/local.mdx index 4e101db..d043956 100644 --- a/apps/docs/get-started/local.mdx +++ b/apps/docs/get-started/local.mdx @@ -142,8 +142,8 @@ Once the app is added you can add the Client ID under `GITHUB_ID`and CLIENT SECR Next, we need to add in the [AWS credentials](https://docs.usesend.com/get-started/create-aws-credentials). Follow the detailed guide to get the AWS credentials with accurate permissions and add them in: ``` -AWS_ACCESS_KEY= -AWS_SECRET_KEY= +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= ``` diff --git a/apps/docs/get-started/set-up-docker.mdx b/apps/docs/get-started/set-up-docker.mdx index ac9c927..1766c5d 100644 --- a/apps/docs/get-started/set-up-docker.mdx +++ b/apps/docs/get-started/set-up-docker.mdx @@ -48,19 +48,19 @@ docker pull ghcr.io/usesend/usesend ``` docker run -d \ -p 3000:3000 \ - -e NEXTAUTH_URL="" - -e NEXTAUTH_SECRET="" - -e DATABASE_URL="" - -e REDIS_URL="" - -e AWS_ACCESS_KEY="" - -e AWS_SECRET_KEY="" - -e AWS_DEFAULT_REGION="" - -e GITHUB_ID="" - -e GITHUB_SECRET="" + -e NEXTAUTH_URL="" \ + -e NEXTAUTH_SECRET="" \ + -e DATABASE_URL="" \ + -e REDIS_URL="" \ + -e AWS_ACCESS_KEY_ID="" \ + -e AWS_SECRET_ACCESS_KEY="" \ + -e AWS_DEFAULT_REGION="" \ + -e GITHUB_ID="" \ + -e GITHUB_SECRET="" \ usesend/usesend ``` -Replace the placeholders with your actual database and aws details. +Replace the placeholders with your actual database and AWS details. 1. Access the useSend application by visiting the URL you provided in the `NEXTAUTH_URL` environment variable in your web browser. diff --git a/apps/docs/self-hosting/overview.mdx b/apps/docs/self-hosting/overview.mdx index 07b2413..2c1a6ed 100644 --- a/apps/docs/self-hosting/overview.mdx +++ b/apps/docs/self-hosting/overview.mdx @@ -21,8 +21,8 @@ useSend depends on AWS SES to send emails and SNS to receive email status. Along Add the following environment variables. ```env - AWS_ACCESS_KEY= - AWS_SECRET_KEY= + AWS_ACCESS_KEY_ID= + AWS_SECRET_ACCESS_KEY= ``` diff --git a/apps/docs/self-hosting/railway.mdx b/apps/docs/self-hosting/railway.mdx index 771325e..0c004dc 100644 --- a/apps/docs/self-hosting/railway.mdx +++ b/apps/docs/self-hosting/railway.mdx @@ -32,8 +32,8 @@ useSend depends on AWS SES to send emails and SNS to receive email status. The R Add the following environment variables in Railway. ```env -AWS_ACCESS_KEY= -AWS_SECRET_KEY= +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= ``` diff --git a/apps/web/.env.test.example b/apps/web/.env.test.example index 1f62dec..58459ee 100644 --- a/apps/web/.env.test.example +++ b/apps/web/.env.test.example @@ -5,8 +5,8 @@ NEXTAUTH_SECRET=test-secret DATABASE_URL=postgresql://usesend:password@127.0.0.1:54329/usesend_test REDIS_URL=redis://127.0.0.1:6380/15 -AWS_ACCESS_KEY=test-access-key -AWS_SECRET_KEY=test-secret-key +AWS_ACCESS_KEY_ID=test-access-key +AWS_SECRET_ACCESS_KEY=test-secret-key AWS_DEFAULT_REGION=us-east-1 NEXT_PUBLIC_IS_CLOUD=true diff --git a/apps/web/src/env.js b/apps/web/src/env.js index 9c37cdd..8e73959 100644 --- a/apps/web/src/env.js +++ b/apps/web/src/env.js @@ -31,8 +31,8 @@ export const env = createEnv({ ), GITHUB_ID: z.string().optional(), GITHUB_SECRET: z.string().optional(), - AWS_ACCESS_KEY: z.string(), - AWS_SECRET_KEY: z.string(), + AWS_ACCESS_KEY_ID: z.string().optional(), + AWS_SECRET_ACCESS_KEY: z.string().optional(), USESEND_API_KEY: z.string().optional(), UNSEND_API_KEY: z.string().optional(), GOOGLE_CLIENT_ID: z.string().optional(), @@ -99,8 +99,8 @@ export const env = createEnv({ NEXTAUTH_URL: process.env.NEXTAUTH_URL, GITHUB_ID: process.env.GITHUB_ID, GITHUB_SECRET: process.env.GITHUB_SECRET, - AWS_ACCESS_KEY: process.env.AWS_ACCESS_KEY, - AWS_SECRET_KEY: process.env.AWS_SECRET_KEY, + AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID || process.env.AWS_ACCESS_KEY, + AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY || process.env.AWS_SECRET_KEY, USESEND_API_KEY: process.env.USESEND_API_KEY, UNSEND_API_KEY: process.env.UNSEND_API_KEY, GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, diff --git a/apps/web/src/server/aws/credentials.ts b/apps/web/src/server/aws/credentials.ts new file mode 100644 index 0000000..551dee8 --- /dev/null +++ b/apps/web/src/server/aws/credentials.ts @@ -0,0 +1,22 @@ +import { env } from "~/env"; + +export function getAwsCredentialOptions() { + const hasKey = !!env.AWS_ACCESS_KEY_ID; + const hasSecret = !!env.AWS_SECRET_ACCESS_KEY; + + if (hasKey !== hasSecret) { + throw new Error( + "AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must both be set or both be omitted" + ); + } + + if (hasKey) { + return { + credentials: { + accessKeyId: env.AWS_ACCESS_KEY_ID!, + secretAccessKey: env.AWS_SECRET_ACCESS_KEY!, + }, + }; + } + return {}; +} diff --git a/apps/web/src/server/aws/ses.ts b/apps/web/src/server/aws/ses.ts index 0fda94c..5b6e9ab 100644 --- a/apps/web/src/server/aws/ses.ts +++ b/apps/web/src/server/aws/ses.ts @@ -17,6 +17,7 @@ import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts"; import { generateKeyPairSync } from "crypto"; import nodemailer from "nodemailer"; import { env } from "~/env"; +import { getAwsCredentialOptions } from "~/server/aws/credentials"; import { EmailContent } from "~/types"; import { logger } from "../logger/log"; import { buildHeaders } from "~/server/utils/email-headers"; @@ -30,10 +31,7 @@ async function getAccountId(region: string) { const stsClient = new STSClient({ region: region, - credentials: { - accessKeyId: env.AWS_ACCESS_KEY, - secretAccessKey: env.AWS_SECRET_KEY, - }, + ...getAwsCredentialOptions(), }); const command = new GetCallerIdentityCommand({}); const response = await stsClient.send(command); @@ -50,10 +48,7 @@ function getSesClient(region: string) { return new SESv2Client({ region: region, endpoint: env.AWS_SES_ENDPOINT, - credentials: { - accessKeyId: env.AWS_ACCESS_KEY, - secretAccessKey: env.AWS_SECRET_KEY, - }, + ...getAwsCredentialOptions(), }); } diff --git a/apps/web/src/server/aws/sns.ts b/apps/web/src/server/aws/sns.ts index 0b99b13..9973532 100644 --- a/apps/web/src/server/aws/sns.ts +++ b/apps/web/src/server/aws/sns.ts @@ -5,15 +5,13 @@ import { DeleteTopicCommand, } from "@aws-sdk/client-sns"; import { env } from "~/env"; +import { getAwsCredentialOptions } from "~/server/aws/credentials"; function getSnsClient(region: string) { return new SNSClient({ endpoint: env.AWS_SNS_ENDPOINT, region: region, - credentials: { - accessKeyId: env.AWS_ACCESS_KEY, - secretAccessKey: env.AWS_SECRET_KEY, - }, + ...getAwsCredentialOptions(), }); } diff --git a/apps/web/src/test/setup/setup-env.ts b/apps/web/src/test/setup/setup-env.ts index 96d56b4..c6f5879 100644 --- a/apps/web/src/test/setup/setup-env.ts +++ b/apps/web/src/test/setup/setup-env.ts @@ -4,8 +4,8 @@ const defaultEnv: Record = { NEXTAUTH_SECRET: "test-secret", DATABASE_URL: "postgresql://usesend:password@127.0.0.1:54329/usesend_test", REDIS_URL: "redis://127.0.0.1:6380/15", - AWS_ACCESS_KEY: "test-access-key", - AWS_SECRET_KEY: "test-secret-key", + AWS_ACCESS_KEY_ID: "test-access-key", + AWS_SECRET_ACCESS_KEY: "test-secret-key", AWS_DEFAULT_REGION: "us-east-1", NEXT_PUBLIC_IS_CLOUD: "true", API_RATE_LIMIT: "2", diff --git a/docker/README.md b/docker/README.md index dfe16e4..2ce157f 100644 --- a/docker/README.md +++ b/docker/README.md @@ -52,15 +52,15 @@ docker run -d \ -e NEXTAUTH_SECRET="" \ -e DATABASE_URL="" \ -e REDIS_URL="" \ - -e AWS_ACCESS_KEY="" \ - -e AWS_SECRET_KEY="" \ + -e AWS_ACCESS_KEY_ID="" \ + -e AWS_SECRET_ACCESS_KEY="" \ -e AWS_DEFAULT_REGION="" \ -e GITHUB_ID="" \ -e GITHUB_SECRET="" \ usesend/usesend ``` -Replace the placeholders with your actual database and aws details. +Replace the placeholders with your actual database and AWS details. 1. Access the useSend application by visiting the URL you provided in the `NEXTAUTH_URL` environment variable in your web browser. diff --git a/docker/prod/compose.yml b/docker/prod/compose.yml index 89bba50..736ceb3 100644 --- a/docker/prod/compose.yml +++ b/docker/prod/compose.yml @@ -54,8 +54,10 @@ services: - DATABASE_URL=${DATABASE_URL:?err} - NEXTAUTH_URL=${NEXTAUTH_URL:?err} - NEXTAUTH_SECRET=${NEXTAUTH_SECRET:?err} - - AWS_ACCESS_KEY=${AWS_ACCESS_KEY:?err} - - AWS_SECRET_KEY=${AWS_SECRET_KEY:?err} + - AWS_ACCESS_KEY=${AWS_ACCESS_KEY:-} + - AWS_SECRET_KEY=${AWS_SECRET_KEY:-} + - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-} + - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-} - AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:?err} - GITHUB_ID=${GITHUB_ID:?err} - GITHUB_SECRET=${GITHUB_SECRET:?err} diff --git a/turbo.json b/turbo.json index a0d6144..7be1b51 100644 --- a/turbo.json +++ b/turbo.json @@ -21,11 +21,17 @@ "GITHUB_SECRET", "AWS_SECRET_KEY", "AWS_ACCESS_KEY", + "AWS_SECRET_ACCESS_KEY", + "AWS_ACCESS_KEY_ID", + "AWS_DEFAULT_REGION", + "AWS_SES_ENDPOINT", + "AWS_SNS_ENDPOINT", "NEXTAUTH_SECRET", "NODE_ENV", "VERCEL_URL", "VERCEL", "SKIP_ENV_VALIDATION", + "DOCKER_OUTPUT", "PORT", "UNSEND_API_KEY", "USESEND_API_KEY", @@ -57,4 +63,4 @@ "cache": false } } -} \ No newline at end of file +}