Token Vault¶
Auth0 feature for persisting and managing upstream refresh tokens on behalf of users, so agents can call third-party APIs (Slack, Google, Facebook, etc.) without re-authenticating the user each session.
What it solves¶
The naive pattern — "just pass the user's access token to the agent" — is an anti-pattern. Tokens expire; the agent will need to re-prompt the user constantly, breaking autonomy. Worse, the agent gets whatever scopes the user's main session has, not a narrow subset.
Token Vault's pattern: once the user connects an upstream provider (Slack, Gmail, etc.) via Auth0, Auth0 stores the refresh token and manages its lifetime. When the agent needs to act, it performs a token exchange — its own agent-client access token → upstream-provider access token with explicitly requested scopes.
The three-way trust triangle¶
The call is not user → upstream. It's:
- User authenticates with agent (ID token + agent-client access token)
- User pre-authorizes connections (
slack,google-calendar,stock-trade) via Auth0 — once - Agent, acting on behalf of user, exchanges its token for an upstream token with specific scopes
- Upstream API sees an access token with scopes = what the user pre-authorized, actor = agent (via
azp/ authorizing-party claim)
This means: the user grants access once per upstream provider. Next session, the agent just does the exchange — no re-consent dance, no prompt flood.
Flow variants¶
- Refresh-token exchange — for traditional web apps / native apps with a backend, Next.js full-stack.
- Access-token exchange — for single-page apps or environments like LangGraph where there's no secure backend; short-lived access tokens only.
- Custom API client — for MCP servers needing to access remote data; Auth0 now supports linked clients (API ↔ client relationships).
Scope restriction is enforced¶
Scopes requested in a token exchange that weren't part of the original connection are silently ignored or rejected. Prompt injection cannot escalate scopes via the token-exchange mechanism — "scopes not part of the connection can never end up in the access token." The authorization happens before the tool executes, in code, not inside the LLM.
Cross-references¶
- auth-for-ai-four-pillars — pillar 2 (call APIs on my behalf)
- agent-as-oauth-client — the modeling choice that enables this
- async-auth-ciba — pillar 3 (confirmation)
- dynamic-client-registration — MCP variant