Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
8 changes: 5 additions & 3 deletions .env.selfhost.example
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ GITHUB_SECRET="<your-github-client-secret>"
GOOGLE_CLIENT_ID="<your-google-client-id>"
GOOGLE_CLIENT_SECRET="<your-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="<your-aws-secret-key>"
AWS_ACCESS_KEY="<your-aws-access-key>"
AWS_ACCESS_KEY_ID="<your-aws-access-key-id>"
AWS_SECRET_ACCESS_KEY="<your-aws-secret-access-key>"



Expand Down
4 changes: 2 additions & 2 deletions apps/web/.env.test.example
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions apps/web/src/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Comment thread
purva-8 marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
USESEND_API_KEY: z.string().optional(),
UNSEND_API_KEY: z.string().optional(),
GOOGLE_CLIENT_ID: z.string().optional(),
Expand Down Expand Up @@ -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,
Expand Down
22 changes: 22 additions & 0 deletions apps/web/src/server/aws/credentials.ts
Original file line number Diff line number Diff line change
@@ -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!,
},
};
Comment on lines +4 to +19
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether AWS_SESSION_TOKEN is modeled and propagated in current PR surface.
# Expected: references in env schema/mapping + credentials helper + (optionally) env examples.
rg -n -C3 '\bAWS_SESSION_TOKEN\b' \
  apps/web/src/env.js \
  apps/web/src/server/aws/credentials.ts \
  apps/web/src/server/aws/ses.ts \
  apps/web/src/server/aws/sns.ts \
  .env.example \
  .env.selfhost.example \
  apps/web/.env.test.example || true

Repository: usesend/useSend

Length of output: 41


🏁 Script executed:

# First, let's find the actual env schema/configuration file
fd -e js -e ts -e json apps/web/src/ | grep -i env | head -20

Repository: usesend/useSend

Length of output: 417


🏁 Script executed:

# Let's examine the credentials.ts file in full
cat -n apps/web/src/server/aws/credentials.ts

Repository: usesend/useSend

Length of output: 713


🏁 Script executed:

# Check the env.js or env schema structure
fd -type f -name "env*" apps/web/src/ | head -10

Repository: usesend/useSend

Length of output: 230


🏁 Script executed:

# Find env configuration files more carefully
find apps/web/src -name "env*" -type f | head -20

Repository: usesend/useSend

Length of output: 79


🏁 Script executed:

# Also search for where env is exported from
rg -l "export.*env" apps/web/src --type ts --type js | head -10

Repository: usesend/useSend

Length of output: 196


🏁 Script executed:

# Look for AWS configuration in existing code
rg "AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY" apps/web/src --type ts --type js -A 2 -B 2

Repository: usesend/useSend

Length of output: 2484


🏁 Script executed:

# Read the full env.js file
cat -n apps/web/src/env.js

Repository: usesend/useSend

Length of output: 7193


🏁 Script executed:

# Check AWS SDK documentation expectations for sessionToken
# Look for any existing usage or patterns of sessionToken in the codebase
rg "sessionToken|session_token" apps/web --type ts --type js

Repository: usesend/useSend

Length of output: 41


🏁 Script executed:

# Check how AWS SDK is being used for SES and SNS
cat -n apps/web/src/server/aws/ses.ts | head -50

Repository: usesend/useSend

Length of output: 1938


🏁 Script executed:

# Look for any auth-related issues or comments in AWS files
rg "auth|sign|credential|temporary|session" apps/web/src/server/aws/ --type ts -i

Repository: usesend/useSend

Length of output: 781


🏁 Script executed:

# Check AWS SDK version/package being used
rg "@aws-sdk|aws-sdk" apps/web/package.json

Repository: usesend/useSend

Length of output: 265


Add AWS_SESSION_TOKEN support to credentials helper for temporary AWS credentials.

AWS SDK v3 requires sessionToken when using temporary credentials (from STS, IAM roles, or AssumeRole). The current implementation only handles accessKeyId and secretAccessKey, which works for long-lived IAM credentials but will fail with temporary credentials.

Add AWS_SESSION_TOKEN as an optional environment variable in env.js schema and runtimeEnv, then conditionally include it in the returned credentials object in credentials.ts. Update test setup and .env.example files as needed.

Suggested patch
 export function getAwsCredentialOptions() {
   const hasKey = !!env.AWS_ACCESS_KEY_ID;
   const hasSecret = !!env.AWS_SECRET_ACCESS_KEY;
+  const hasSessionToken = !!env.AWS_SESSION_TOKEN;

   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!,
+        ...(hasSessionToken ? { sessionToken: env.AWS_SESSION_TOKEN! } : {}),
       },
     };
   }
   return {};
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/server/aws/credentials.ts` around lines 4 - 19, The credentials
helper currently only returns accessKeyId and secretAccessKey, which breaks
temporary AWS creds; add support for AWS_SESSION_TOKEN by adding
AWS_SESSION_TOKEN to your env.js schema and runtimeEnv, then update the return
object in apps/web/src/server/aws/credentials.ts to conditionally include
sessionToken (e.g., when env.AWS_SESSION_TOKEN is present, add
credentials.sessionToken = env.AWS_SESSION_TOKEN) alongside accessKeyId and
secretAccessKey, and update test fixtures and .env.example to include an
optional AWS_SESSION_TOKEN entry so temporary credential flows are exercised.

}
return {};
}
11 changes: 3 additions & 8 deletions apps/web/src/server/aws/ses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 "./credentials";
import { EmailContent } from "~/types";
import { logger } from "../logger/log";
import { buildHeaders } from "~/server/utils/email-headers";
Expand All @@ -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);
Expand All @@ -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(),
});
}

Expand Down
6 changes: 2 additions & 4 deletions apps/web/src/server/aws/sns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import {
DeleteTopicCommand,
} from "@aws-sdk/client-sns";
import { env } from "~/env";
import { getAwsCredentialOptions } from "./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(),
});
}

Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/test/setup/setup-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ const defaultEnv: Record<string, string> = {
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",
Expand Down