ai

Auth0 Token Vault: Secure Token Exchange for AI Agents

Learn how Auth0 Token Vault uses OAuth 2.0 Token Exchange to provide secure, delegated access, letting AI agents act on a user's behalf without handling refresh tokens.

AI agents are quickly moving beyond simple chat interfaces. They are now expected to take real actions on behalf of users, such as scheduling meetings in Google Calendar, posting updates to Slack, pulling data from Salesforce, or reading files from Dropbox. To deliver on this promise, agents need more than intelligence. They need connected apps. That means they need to securely access third-party APIs on behalf of the user, using the user's identity and permissions rather than a generic API key. Even more important is to have a zero-trust mechanism that gives control to developers rather than the agents.

The difficulty is that making an AI agent act on behalf of a user is not straightforward. A common but flawed approach is to store provider refresh tokens directly in your database and let the agent use them whenever action is required. This is brittle, hard to scale, and risky. Refresh tokens can expire, be revoked, or be abused if mishandled. On top of that, you'd have to manage token rotation, MFA policies, and multiple user accounts, which quickly becomes a maze of exceptions and security gaps. Without the right foundation, delegated access for AI agents becomes a liability instead of an enabler.

Another common approach is using API keys for the services, but that has its own drawbacks, like the loss of user context and granularity. API keys are often scoped with more privileges than required, are long-lived, and do not give the end users any control over what they want to share.

Understanding federated identity

Federated identity isn't a standard or protocol but rather the application of the OAuth 2.0 framework within a broader federation architecture. It enables users to sign in to a service using an identity from a separate, trusted provider. In this model, one system acts as the Identity Provider (IdP), like Auth0, which is responsible for authenticating the user. Another system, the Service Provider (SP) or application, like Google or Microsoft, trusts the IdP to verify the user's identity. The most common example is social login, where you use your Google account to log into a third-party application. This process allows for a seamless user experience by eliminating the need for separate credentials for every service, while securely delegating the authentication process to a trusted authority like Auth0.

Identity provider authentication flow diagram

Understanding OAuth 2.0 token exchange and the On-Behalf-Of model

OAuth 2.0 Token Exchange is defined in RFC 8693 as a standard way to trade one token for another. Instead of handing a client full control of a long-lived credential, the client can request a new token with different attributes that are better suited for the task at hand. This process is often called the "On-Behalf-Of" (OBO) flow.

Consider a common scenario: a user interacts with a web application (the client), which calls API A. API A then needs to call a backend service, API B, to fulfill the request. How should API A authenticate to API B?

  1. Pass the original access token? This is problematic. The original token was issued for API A (its "audience"). API B should reject it because it was not the intended recipient. Furthermore, the original token might contain broad permissions that API B doesn't need, violating the principle of least privilege.
  2. Use its own credentials? API A could use the client credentials grant to get its own token for API B. However, this loses the user's context. API B would see the call as coming from API A, with no link to the original user who initiated the action.

Token Exchange solves this by allowing API A to act as a client and exchange the user's token for a new one, specifically meant for API B.

RFC 8693 formalizes this by introducing a new grant type:

urn:ietf:params:oauth:grant-type:token-exchange

A request using this grant type includes a subject_token representing the original user. It can also include an actor_token to represent the party that is currently requesting to act.

This distinction enables two models:

  • Impersonation: The client presents only a subject_token. The authorization server issues a new token that still represents the original user (the subject), but is now scoped for a new audience (the downstream service). The intermediate service that performed the exchange is essentially invisible in the new token.
  • Delegation: The client presents both a subject_token (the user) and an actor_token (itself). The authorization server issues a "composite" token that represents the actor acting on behalf of the subject. This is often materialized through an act (actor) claim within the new JWT, creating a clear audit trail of the delegation chain.

This is the mechanism that lets one service or agent call another service using the authority of the end user. In practice, this is how, for example, an AI agent could schedule a meeting in a user’s calendar or fetch their files without needing to hold on to sensitive refresh tokens. The exchange request clearly expresses that the agent is the actor, the user remains the subject, and the provider issues a fresh access token scoped to the delegated action.

While the RFC provides a standardized framework, implementing it correctly presents several security challenges and can create major vulnerabilities. For example:

  • Mishandling Secrets: Your application must now manage multiple sensitive credentials. Storing your service’s secrets or the newly acquired credentials insecurely is a common vulnerability that attackers can exploit.
  • Privilege Escalation: A misconfiguration on the rules of your identity server, a simple error could allow a low-power service to trade a limited token for one with admin-level permissions, granting that service far more access than it should.
  • Creating Overly Permissive Tokens: The goal is to get a new token that is strictly limited to one specific downstream service; however, if you fail to enforce this “audience” restriction, you may allow tokens to be used in services that were not intended.

The most secure approach is to use a dedicated service that manages these sensitive tokens for you, so your application never has to touch them.

Introducing Auth0 Token Vault: The secure solution for AI agents

Token Vault identity authentication flow diagram

Auth0 Token Vault solves this problem by implementing the federated identity architecture across connections and acting as a secure, centralized store for provider tokens. When a user signs in with a third-party connection such as Google, Microsoft, Slack, or GitHub, the resulting access and refresh tokens are placed into a "token vault", which is a secure storage, powered by your Auth0 SDK of choice. Your application or agent never touches those provider refresh tokens directly. Instead, it only holds an Auth0 access token and/or refresh token, which is handled by our SDKs.

Example of a Token Vault authentication flow diagram

When the agent needs to call a provider API on behalf of the user, it performs a simple exchange. The Auth0 access token or refresh token is traded for a fresh federated access token from the Token Vault. That token is scoped to the provider and ready to use in API calls. This design keeps long-lived provider credentials off your infrastructure, reduces security risk, and lets your AI agents act on behalf of users with minimal custom logic.

The best part is that you do not have to hand-craft the token exchange calls yourself. Auth0 provides SDK helpers that let you request a provider access token in just a few lines of code.

For example, using the NextJS SDK:

import { NextResponse } from "next/server";

import { auth0 } from "./lib/auth0"; // Adjust path if your auth0 client is elsewhere

export async function GET() {
 try {
   const token = await auth0.getAccessTokenForConnection({
     connection: "google-oauth2"
   });
   // call external API with token...
 } catch (err) {
   // err will be an instance of AccessTokenError if an access token could not be obtained
 }

 return NextResponse.json({
   message: "Success!"
 });
}

This approach collapses a complex sequence of grant requests, token storage, and refresh logic into a single developer-friendly function call. It allows teams to focus on building AI agent behaviors while Auth0 handles the token lifecycle behind the scenes.

Token Vault for AI agents

AI agents and GenAI apps need to act inside the tools users rely on every day. With Token Vault, they can securely fetch provider access tokens at runtime and call APIs on behalf of the user without managing refresh tokens or custom OAuth code.

Auth0 SDKs provide ready-made integrations for popular agentic frameworks like LangChain and Vercel AI. This means you can register an external connection, configure scopes, and then let your agent request tokens with a simple helper function. The agent gets just enough access to complete the task, and Token Vault takes care of storage, rotation, and security.

For example, using Vercel AI, you can easily retrieve an access token to access the user's Google Calendar as follows:

// src/lib/auth0-ai.ts
import { Auth0AI, getAccessTokenForConnection } from "@auth0/ai-vercel";
import { auth0 } from "./auth0";

// Get the access token for a connection via Auth0
export const getAccessToken = async () => getAccessTokenForConnection();

const auth0AI = new Auth0AI();

// Connection for Google services
export const withGoogleConnection = auth0AI.withTokenForConnection({
 connection: "google-oauth2",
 scopes: ["https://www.googleapis.com/auth/calendar.events"],
 refreshToken: async () => {
   const session = await auth0.getSession();
   return session?.tokenSet?.refreshToken;
 },
});

// src/lib/tools/google-calendar.ts
import { tool } from 'ai';
import { endOfDay, formatISO, startOfDay } from 'date-fns';
import { GaxiosError } from 'gaxios';
import { google } from 'googleapis';
import { z } from 'zod';
import { FederatedConnectionError } from '@auth0/ai/interrupts';

import { getAccessToken, withGoogleConnection } from '../auth0-ai';

export const getCalendarEventsTool = withGoogleConnection(
 tool({
   description: `Get calendar events for a given date from the user's Google Calendar`,
   parameters: z.object({
     date: z.coerce.date(),
   }),
   execute: async ({ date }) => {
     // Get the access token from Auth0 AI
     const accessToken = await getAccessToken();

     // Google SDK
     try {
       const calendar = google.calendar('v3');
       const auth = new google.auth.OAuth2();

       auth.setCredentials({
         access_token: accessToken,
       });

       // Get events for the entire day
       const response = await calendar.events.list({
         auth,
         calendarId: 'primary',
         timeMin: formatISO(startOfDay(date)),
         timeMax: formatISO(endOfDay(date)),
         singleEvents: true,
         orderBy: 'startTime',
         maxResults: 50,
       });

       const events = response.data.items || [];

       return {
         date: formatISO(date, { representation: 'date' }),
         eventsCount: events.length,
         // ...
       };
     } catch (error) {
       if (error instanceof GaxiosError) {
          // In case the user is unauthorized, we can trigger a special error, we can then capture in the UI to ask the user for authorization
         if (error.status === 401) {
           throw new FederatedConnectionError(`Authorization required to access the Federated Connection`);
         }
       }

       throw error;
     }
   },
 }),
);

For full implementation details and SDK examples for multiple frameworks and languages, see the Auth0 AI docs.

Frequently asked questions (FAQs)

Is Auth0 Token Vault the same as OAuth 2.0 Token Exchange?

Not exactly. Token Exchange is the open standard defined in RFC 8693. Token Vault implements the protocol defined in the RFC, following the “on behalf of” pattern, but adds secure storage for provider tokens and SDK helpers so you can use it in just a few lines of code.

Which providers can I use with Token Vault today?

Today, we support over 30 providers between social and enterprise connections. You can check out our docs for a full list of integrations.

Do you need refresh tokens to use Token Vault?

No, you can use Token Vault with either refresh tokens or access tokens. If your application uses refresh tokens, you can use them for the Token Vault calls, as it's the simpler approach. If you can't use a refresh token, then you can configure a Custom API client in Auth0. The Custom API Client allows your API server to perform token exchanges using access tokens instead of refresh tokens. This client enables Token Vault to exchange an access token for an external API access token (e.g., Google Calendar API).

What happens if the user revokes access?

The token vault for that connection is invalidated. Future exchange requests will fail until the user re-consents, keeping behavior predictable and secure.

How long do federated access tokens last?

Federated access tokens are short-lived. Their exact lifetime depends on the upstream provider (Google, Microsoft, Slack, etc.), but Token Vault will refresh them as needed using the stored provider refresh token.

Can Token Vault be used to approve requests before they run?

Approval flows are not handled by Token Vault directly. If you need a human-in-the-loop check before an agent performs a sensitive action, you should combine Token Vault with asynchronous authorization. This pauses the agent request until the user explicitly approves on another trusted device. You can learn more in the Auth0 docs on asynchronous authorization.

Where’s the best place to get started?

Check out the Auth0 AI docs for complete examples with LangChain, LlamaIndex, and web SDKs.