Skip to content
'; user_status_content.firstChild.appendChild(avatarContainer); } else { // Placeholder for LoggedOutUserMenu let loggedOutContainer = document.createElement('div'); // if LoggedOutUserMenu fallback let userBtn = document.createElement('button'); userBtn.style.width = "33px"; userBtn.style.height = "33px"; userBtn.style.display = "flex"; userBtn.style.alignItems = "center"; userBtn.style.justifyContent = "center"; userBtn.style.color = "var(--ds-gray-900)"; userBtn.style.border = "1px solid var(--ds-gray-300)"; userBtn.style.borderRadius = "100%"; userBtn.style.cursor = "pointer"; userBtn.style.background = "transparent"; userBtn.style.padding = "0"; // user icon ( from geist) let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('data-testid', 'geist-icon'); svg.setAttribute('height', '16'); svg.setAttribute('stroke-linejoin', 'round'); svg.setAttribute('style', 'color:currentColor'); svg.setAttribute('viewBox', '0 0 16 16'); svg.setAttribute('width', '16'); let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path.setAttribute('fill-rule', 'evenodd'); path.setAttribute('clip-rule', 'evenodd'); path.setAttribute('d', 'M7.75 0C5.95507 0 4.5 1.45507 4.5 3.25V3.75C4.5 5.54493 5.95507 7 7.75 7H8.25C10.0449 7 11.5 5.54493 11.5 3.75V3.25C11.5 1.45507 10.0449 0 8.25 0H7.75ZM6 3.25C6 2.2835 6.7835 1.5 7.75 1.5H8.25C9.2165 1.5 10 2.2835 10 3.25V3.75C10 4.7165 9.2165 5.5 8.25 5.5H7.75C6.7835 5.5 6 4.7165 6 3.75V3.25ZM2.5 14.5V13.1709C3.31958 11.5377 4.99308 10.5 6.82945 10.5H9.17055C11.0069 10.5 12.6804 11.5377 13.5 13.1709V14.5H2.5ZM6.82945 9C4.35483 9 2.10604 10.4388 1.06903 12.6857L1 12.8353V13V15.25V16H1.75H14.25H15V15.25V13V12.8353L14.931 12.6857C13.894 10.4388 11.6452 9 9.17055 9H6.82945Z'); path.setAttribute('fill', 'currentColor'); svg.appendChild(path); userBtn.appendChild(svg); loggedOutContainer.appendChild(userBtn); loggedOutContainer.style.display = 'flex'; loggedOutContainer.style.gap = '8px'; loggedOutContainer.style.alignItems = 'center'; user_status_content.firstChild.appendChild(loggedOutContainer); } })();
Menu

Connect to Amazon Web Services (AWS)

Last updated October 27, 2025

Secure backend access with OIDC federation is available on all plans

To understand how AWS supports OIDC, and for a detailed user guide on creating an OIDC identity provider with AWS, consult the AWS OIDC documentation.

    1. Navigate to the AWS Console
    2. Navigate to IAM then Identity Providers
    3. Select Add Provider
    4. Select OpenID Connect from the provider type
    5. Enter the Provider URL, the URL will depend on the issuer mode setting:
      • Team: https://oidc.vercel.com/[TEAM_SLUG], replacing [TEAM_SLUG] with the path from your Vercel team URL
      • Global: https://oidc.vercel.com
    6. Enter https://vercel.com/[TEAM_SLUG] in the Audience field, replacing [TEAM_SLUG] with the path from your Vercel team URL
    7. Select Add Provider
    Add provider values for the Global issuer mode setting. For the Team issuer mode setting, set the Provider URL to https://vercel.com/[TEAM_SLUG]
    Add provider values for the Global issuer mode setting. For the Team issuer mode setting, set the Provider URL to https://vercel.com/[TEAM_SLUG]
  1. To use AWS OIDC Federation you must have an IAM role. IAM roles require a "trust relationship" (also known as a "trust policy") that describes which "Principal(s)" are allowed to assume the role under certain "Condition(s)".

    Here is an example of a trust policy using the Team issuer mode:

    trust-policy.json
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Federated": "arn:aws:iam::[YOUR AWS ACCOUNT ID]:oidc-provider/oidc.vercel.com/[TEAM_SLUG]"
          },
          "Action": "sts:AssumeRoleWithWebIdentity",
          "Condition": {
            "StringEquals": {
              "oidc.vercel.com/[TEAM_SLUG]:sub": "owner:[TEAM SLUG]:project:[PROJECT NAME]:environment:production",
              "oidc.vercel.com/[TEAM_SLUG]:aud": "https://vercel.com/[TEAM SLUG]"
            }
          }
        }
      ]
    }

    The above policy's conditions are quite strict. It requires the aud sub sub claims to match exactly, but it's possible to configure less strict trust policies conditions:

    trust-policy.json
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Federated": "arn:aws:iam::[YOUR AWS ACCOUNT ID]:oidc-provider/oidc.vercel.com/[TEAM_SLUG]"
          },
          "Action": "sts:AssumeRoleWithWebIdentity",
          "Condition": {
            "StringEquals": {
              "oidc.vercel.com/[TEAM_SLUG]:aud": "https://vercel.com/[TEAM SLUG]"
            },
            "StringLike": {
              "oidc.vercel.com/[TEAM_SLUG]:sub": [
                "owner:[TEAM SLUG]:project:*:environment:preview",
                "owner:[TEAM SLUG]:project:*:environment:production"
              ]
            }
          }
        }
      ]
    }

    This policy allows any project matched by the * that are targeted to preview and production but not development.

  2. Once you have created the role, copy the role's ARN and declare it as an environment variable in your Vercel project with key name AWS_ROLE_ARN.

    .env.local
    AWS_ROLE_ARN=arn:aws:iam::accountid:user/username

    You are now ready to connect to your AWS resource in your project's code. Review the examples below.

In the following examples, you create a Vercel function in the Vercel project where you have defined the OIDC role ARN environment variable. The function will connect to a specific resource in your AWS backend using OIDC and perform a specific action using the AWS SDK.

Install the following packages:

Terminal
pnpm i @aws-sdk/client-s3 @vercel/oidc-aws-credentials-provider

In the API route for the function, use the AWS SDK for JavaScript to list objects in an S3 bucket with the following code:

/api/aws-s3/route.ts
import * as S3 from '@aws-sdk/client-s3';
import { awsCredentialsProvider } from '@vercel/oidc-aws-credentials-provider';
 
const AWS_REGION = process.env.AWS_REGION!;
const AWS_ROLE_ARN = process.env.AWS_ROLE_ARN!;
const S3_BUCKET_NAME = process.env.S3_BUCKET_NAME!;
 
// Initialize the S3 Client
const s3client = new S3.S3Client({
  region: AWS_REGION,
  // Use the Vercel AWS SDK credentials provider
  credentials: awsCredentialsProvider({
    roleArn: AWS_ROLE_ARN,
  }),
});
 
export async function GET() {
  const result = await s3client.send(
    new S3.ListObjectsV2Command({
      Bucket: S3_BUCKET_NAME,
    }),
  );
  return result?.Contents?.map((object) => object.Key) ?? [];
}

Vercel sends the OIDC token to the SDK using the awsCredentialsProvider function from @vercel/oidc-aws-credentials-provider.

Install the following packages:

Terminal
pnpm i @aws-sdk/rds-signer @vercel/oidc-aws-credentials-provider pg

In the API route for the function, use the AWS SDK for JavaScript to perform a database SELECT query from an AWS RDS instance with the following code:

/api/aws-rds/route.ts
import { awsCredentialsProvider } from '@vercel/oidc-aws-credentials-provider';
import { Signer } from '@aws-sdk/rds-signer';
import { Pool } from 'pg';
 
const RDS_PORT = parseInt(process.env.RDS_PORT!);
const RDS_HOSTNAME = process.env.RDS_HOSTNAME!;
const RDS_DATABASE = process.env.RDS_DATABASE!;
const RDS_USERNAME = process.env.RDS_USERNAME!;
const AWS_REGION = process.env.AWS_REGION!;
const AWS_ROLE_ARN = process.env.AWS_ROLE_ARN!;
 
// Initialize the RDS Signer
const signer = new Signer({
  // Use the Vercel AWS SDK credentials provider
  credentials: awsCredentialsProvider({
    roleArn: AWS_ROLE_ARN,
  }),
  region: AWS_REGION,
  port: RDS_PORT,
  hostname: RDS_HOSTNAME,
  username: RDS_USERNAME,
});
 
// Initialize the Postgres Pool
const pool = new Pool({
  password: signer.getAuthToken,
  user: RDS_USERNAME,
  host: RDS_HOSTNAME,
  database: RDS_DATABASE,
  port: RDS_PORT,
});
 
// Export the route handler
export async function GET() {
  try {
    const client = await pool.connect();
    const { rows } = await client.query('SELECT * FROM my_table');
    return Response.json(rows);
  } finally {
    client.release();
  }
}

Was this helpful?

supported.