OIDC Federation Reference

Review helper libraries to help you connect with your backend and understand the structure of an OIDC token.
Table of Contents

Secure backend access with OIDC federation is available on all plans

Vercel provides helper libraries to make it easier to exchange the OIDC token for short-lived credentials with your cloud provider. They are available from the @vercel/functions package on npmjs.com.

awsCredentialsProvider() is a helper function that returns a function that can be used as the credentials property of the AWS SDK client. It exchanges the OIDC token for short-lived credentials with AWS by calling the AssumeRoleWithWebIdentity operation.

import { awsCredentialsProvider } from '@vercel/functions/oidc';
import * as s3 from '@aws-sdk/client-s3';
 
const s3client = new s3.S3Client({
  region: proces.env.AWS_REGION!,
  credentials: awsCredentialsProvider({
    roleArn: process.env.AWS_ROLE_ARN!,
  }),
});

getVercelOidcToken() returns the OIDC token from the VERCEL_OIDC_TOKEN environment variable in builds and local development environments or the x-vercel-oidc-token in Vercel functions.

import { getVercelOidcToken } from '@vercel/functions/oidc';
import { ClientAssertionCredential } from '@azure/identity';
import { CosmosClient } from '@azure/cosmos';
 
const credentialsProvider = new ClientAssertionCredential(
  process.env.AZURE_TENANT_ID,
  process.env.AZURE_CLIENT_ID,
  getVercelOidcToken,
);
 
const cosmosClient = new CosmosClient({
  endpoint: process.env.COSMOS_DB_ENDPOINT,
  aadCredentials: credentialsProvider,
});

In the Vercel function environments, you cannot execute the getVercelOidcToken() function directly at the module level because the token is only available in the Request object as the x-vercel-oidc-token header.

If you change the name of your team or project, the claims within the OIDC token will reflect the new names. This can affect your trust and access control policies. You should consider this when you plan to rename your team or project and update your policies accordingly.

AWS roles can support multiple conditions so you can allow access to both the old and new team and project names. The following example shows when the issuer mode is set to global:

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

If your project is using the team issuer mode, you will need to create a new OIDC provider and add another statement to the trust policy:

aws-trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "OldTeamName",
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::[YOUR AWS ACCOUNT ID]:oidc-provider/oidc.vercel.com/[OLD_TEAM_SLUG]"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.vercel.com/[OLD_TEAM_SLUG]:aud": [
            "https://vercel.com/[OLD_TEAM_SLUG]"
          ],
          "oidc.vercel.com/[OLD_TEAM_SLUG]:sub": [
            "owner:[OLD_TEAM_SLUG]:project:[OLD_PROJECT_NAME]:environment:production"
          ]
        }
      }
    },
    {
      "Sid": "NewTeamName",
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::[YOUR AWS ACCOUNT ID]:oidc-provider/oidc.vercel.com/[NEW_TEAM_SLUG]"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.vercel.com/[NEW_TEAM_SLUG]:aud": [
            "https://vercel.com/[NEW_TEAM_SLUG]"
          ],
          "oidc.vercel.com/[NEW_TEAM_SLUG]:sub": [
            "owner:[NEW_TEAM_SLUG]:project:[NEW_PROJECT_NAME]:environment:production"
          ]
        }
      }
    }
  ]
}

You can validate OpenID Connect tokens by using the issuer's OpenID Connect Discovery Well Known location, which is either https://oidc.vercel.com/.well-known/openid-configuration or https://oidc.vercel.com/[TEAM_SLUG]/.well-known/openid-configuration depending on the issuer mode in your project settings. There, you can find a property called jwks_uri which provides a URI to Vercel's public JSON Web Keys (JWKs). You can use the corresponding JWK identified by kid to verify tokens that are signed with the same kid in the token's header.

// Header:
{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "example-key-id"
}
// Claims:
{
  "iss": "https://oidc.vercel.com/acme",
  "aud": "https://vercel.com/acme",
  "sub": "owner:acme:project:acme_website:environment:production",
  "iat": 1718885593,
  "nfb": 1718885593,
  "exp": 1718889193,
  "owner": "acme",
  "owner_id": "team_7Gw5ZMzpQA8h90F832KGp7nwbuh3",
  "project": "acme_website",
  "project_id": "prj_7Gw5ZMBpQA8h9GF832KGp7nwbuh3",
  "environment": "production"
}

This is a list of standard tokens that you can expect from an OpenID Connect JWT:

ClaimKindDescription
issIssuerWhen using the team issuer mode, the issuer is set to https://oidc.vercel.com/[TEAM_SLUG]
When using the global issuer mode, the issuer is set to https://oidc.vercel.com
audAudienceThe audience is set to https://vercel.com/[TEAM_SLUG]
subSubjectThe subject is set to owner:[TEAM_SLUG]:project:[PROJECT_NAME]:environment:[ENVIRONMENT]
iatIssued atThe time the token was created
nbfNot beforeThe token is not valid before this time
expExpires atThe time the token has or will expire. preview and production tokens expire one hour after creation, development tokens expire in 12 hours.

These claims provide more granular access control:

ClaimDescription
ownerThe team slug, e.g. acme
owner_idThe team ID, e.g. team_7Gw5ZMzpQA8h90F832KGp7nwbuh3
projectThe project name, e.g. acme_website
project_idThe project ID, e.g. prj_7Gw5ZMBpQA8h9GF832KGp7nwbuh3
environmentThe environment: development or preview or production

These headers are standard to the JWT tokens:

HeaderKindDescription
algAlgorithmThe algorithm used by the issuer
kidKey IDThe identifier of the key used to sign the token
typTypeThe type of token, this is set to jwt.
Last updated on October 23, 2024