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

Tokens

Last updated February 26, 2026

There are three tokens your application will work with when using Sign in with Vercel:

The ID Token is a signed JWT that contains information about the user who is signing in. When using ID Token claims, your application should both decode the token and verify its signature against the public JWKS endpoint to ensure authenticity. The ID Token does not give access to Vercel resources, it only proves the user's identity.

ID Token payload example
{
  "iss": "https://vercel.com",
  "sub": "345e869043f1e55f8bdc837c",
  "aud": "cl_be6c3c8b9f340d4a20feefab2862a49a",
  "exp": 1519948800,
  "iat": 1519945200,
  "nbf": 1519945200,
  "jti": "50e67781-c8b6-4391-98d1-89d755bb095a",
  "name": "John Doe",
  "preferred_username": "john-doe",
  "picture": "https://api.vercel.com/www/avatar/00159aa4c88348dedc91a456b457d1baa48df6d",
  "email": "[email protected]",
  "nonce": "a4a522fa63f9cea6eeb1"
}

The code below shows how to decode and validate an ID token using the jose library:

import { jwtVerify, createRemoteJWKSet } from 'jose';
 
const jwkSet = createRemoteJWKSet(
  new URL('https://vercel.com/.well-known/jwks'),
);
 
async function decodeIdToken(idToken: string) {
  const { payload } = await jwtVerify(idToken, jwkSet, {
    issuer: 'https://vercel.com',
    audience: [process.env.NEXT_PUBLIC_VERCEL_APP_CLIENT_ID],
  });
 
  return payload;
}

Vercel's IdP generates OpenID Connect tokens that contain various JWT claims depending on the requested scopes:

ClaimTypeDescriptionExample
issstringIssuer - The server that issued the token"https://vercel.com"
substringSubject - Unique identifier for the authenticated user"345e869043f1e55f8bdc837c"
audstringAudience - The ID of the Vercel application"cl_be6c3c8b9f340d4a20feefab2862a49a"
expnumberExpiration time - Unix timestamp when the token expires1519948800
iatnumberIssued at - Unix timestamp when the token was issued1519945200
nbfnumberNot before - Unix timestamp before which the token is invalid1519945200
jtistringJWT ID - Unique identifier for this specific token"50e67781-c8b6-4391-98d1-89d755bb095a"
noncestringCryptographic nonce for replay protection"a4a522fa63f9cea6eeb1"

Depending on the scopes requested the following claims will be included in the ID Token:

ScopeClaimsDescriptionExample
profilenameThe user's full display name"John Doe"
profilepreferred_usernameThe user's username on Vercel"john-doe"
profilepictureURL to the user's avatar image (only if user has an avatar)"https://api.vercel.com/www/avatar/avatar-42…"
emailemailThe user's email address"[email protected]"

The Access Token grants your application permission to access specific resources on Vercel on behalf of the user trying to sign in. It is used to authenticate requests to Vercel's REST API. Access Tokens use an opaque format that ensures they are not readable by humans, are secure, and have server side validation to ensure they are not tampered with.

Access Token example
vca_BQuu9ChDu3n6Pfh6YQnCshpoYkWDSFKogLqmBtQ0tC8NAA5rXt340sjz

Access Tokens are valid for one hour. Refresh Tokens can be exchanged to receive new Access Tokens when they expire. Refresh Tokens are valid for 30 days. When you exchange a Refresh Token for an Access Token, you also receive a new Refresh Token.

When using the Access Token in your application code to fetch the user's data, it must be included in the Authorization header as a Bearer token.

Fetching the users data with the Access Token
const result = await fetch('https://api.vercel.com/v2/user', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Refresh Tokens allow your application to get a new Access Token without asking the user to sign in again. The token lasts for 30 days and rotates each time it's used. When the Access Token expires or is about to expire, a Refresh Token can be exchanged for a new Access and Refresh token pair.

Each Refresh Token is single use and automatically rotated on exchange, invalidating the previous token.

Refresh Tokens use an opaque format that ensures they are not readable by humans, are secure, and have server side validation to ensure they are not tampered with.

Refresh Token example
vcr_BQuu9ChDu3n6Pfh6YQnCshpoYkWDSFKogLqmBtQ0tC8NAA5rXt340sjz

Access and Refresh Tokens are sensitive credentials and should be stored securely. Never expose them to the client side of your application.

  • They can be stored in cookies with the HttpOnly, Secure and SameSite=Strict attributes
  • They can be stored in a database with encryption
  • Revoke tokens immediately if you suspect they have been compromised, either by calling the Revoke Token Endpoint or by invalidating all tokens for your application from the dashboard. See manage Sign in with Vercel from the dashboard for more information.

Was this helpful?

supported.