AI Engineer Code 2025
Identity for AI Agents
Read the talk
Identity for AI Agents: From Login to Delegated Action
Build a trading assistant that can identify its user, read an authorized portfolio, expose protected MCP tools, and request approval before placing an order.
Before you start: Familiarity with TypeScript, Next.js routes, HTTP APIs, and basic OAuth concepts will help you follow the implementation.
What authorizes an agent to act?
How do you authorize an AI agent—and the MCP servers it calls—to act for a user? A login is only the beginning. The workshop introduces two mechanisms for the next steps: Token Vault for delegated upstream API access, and Async Auth for requesting human approval. Patrick describes several capabilities as newly generally available during the workshop week. The material, adapted from work prepared by architect Abhishek, also places fine-grained authorization in the broader architecture, while leaving the open-source FGA project and its deeper access-control model for another talk.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Identity must survive beyond the chat session
Okta and Auth0’s ambition to let people safely use technology predates agents, but the threats change with the technology. Carlos uses the OWASP LLM Top 10 as the threat context, then broadens the application model: interactive chatbots and code editors are followed by task runners, autonomous agents, and eventually agents communicating with other agents. Once work continues without a person watching the chat window, identity and permission cannot depend on a fresh conversational instruction at every step.
Four requirements follow:
- Know the user. Establish whose identity and permissions apply to the operation.
- Call APIs on the user’s behalf. Give the agent a controlled way to consume external services.
- Request confirmation. Let the agent reach the person when an operation needs supervision.
- Constrain resource access. Limit access to the intended resources, collections, or documents.
An employee also represents an organization, so company policy must govern the delegation alongside the employee’s choices. An audience question makes the identity boundary explicit: in this scenario, the subject is a human—perhaps an employee, owner, or administrator. The workshop covers both who may use an agent and what that agent may access. Those are separate authorization decisions.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Two mechanisms: request approval and obtain upstream credentials
Async Auth starts with the agent. A long-running process reaches a purchase or another operation that the application considers risky, then initiates an authorization request. Its foundation is Client-Initiated Backchannel Authentication, or CIBA, an OpenID Connect specification that separates the initiating client from the user’s authentication interaction. In the workshop’s transaction-approval flow, the SDK sends structured details to the user; approval returns an access token carrying the approved details. CIBA provides the backchannel, while Auth0’s combination with rich authorization details supplies the transaction-specific information.
Token Vault addresses a different problem: an agent needs credentials for an upstream service, such as Slack or Facebook, without managing that service’s token lifecycle itself. Auth0 persists upstream refresh tokens and granted scopes, manages token lifetimes, and exchanges an appropriate user token for an upstream access token. The right exchange depends on where the agent runs.
| Application shape | Exchange approach described |
|---|---|
| SPA or agent behind an external API | Short-lived access token |
| Embedded Next.js agent or suitable native app | Refresh-token flow |
| MCP server or asynchronous component | Custom API client for upstream access |
Patrick uses LangGraph’s externally exposed API and CLI protocol to illustrate the access-token case. An embedded agent in a traditional web application has a different credential boundary. Across these arrangements, the agent is modeled as an OAuth client and the API as a resource server. The subject_token_type identifies whether an access or refresh token is being exchanged; the subject_token is the user-associated token supplied to that exchange.
The calendar example adds an important interruption path. When an agent needs access to Google Calendar, the SDK can interrupt the LangGraph flow and indicate that additional scopes must be requested. After that permission step, Token Vault performs the exchange and supplies an access token for the upstream provider. The interruption gives the application a place to obtain consent before attempting the protected call.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Build the trading assistant in layers
The MCP architecture adds another boundary. The agent is a client of the MCP server; that server is a protected resource for the agent and separately acts as a client when calling upstream APIs. Patrick introduces Auth0’s MCP support as a newly launched preview, including dynamic client registration. The workshop hosts the Next.js agent and MCP tools together on Vercel, allowing the agent to call the MCP server and the server to reach third-party services.
The concrete application is DemoTradePro, a fictional trading service with a web interface, a stock API, and an upstream identity provider. A root tenant supplies that identity, and a federated OIDC connection links the downstream application to it. The build begins with a simple chatbot and adds identity and authorization incrementally.
To follow the implementation, prepare an Auth0 tenant and application credentials, Node.js, pnpm, the Auth0 CLI, and Terraform. The workshop uses both CLI commands and Terraform for configuration. Its starter application initially has no upstream tools; Patrick uses OpenAI for inference and requires an OpenAI API key, with a completed code branch to be supplied at the end.
The first running chatbot exposes the limitation directly. It can answer ordinary generative questions, but it cannot identify the user or retrieve data from the trading service. Adding a stock-price tool through the Vercel AI SDK changes the second capability. After an audience request for a clearer overview, the walkthrough returns to the resulting behavior: the chatbot now answers trading-information questions through the upstream API. This first endpoint is public, so it requires no authentication or authorization. Tool access can make an agent useful before it gives the agent any private authority.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Login establishes identity; connected accounts grant access
Private portfolio data requires a three-party relationship: the user, the agent, and the upstream trading service. Logging into the agent establishes only the first relationship. The login flow provides identity and access tokens for that application, but upstream access requires a connected account and an appropriate token exchange. Once the trading account is connected, Token Vault stores the upstream refresh token, retains usable access tokens until expiration, and uses the refresh-token grant to obtain replacements when needed.
The tenant setup enables connected accounts through the My Account API, configures the application’s grant types, and creates the OIDC connection. In the Next.js application, Auth0 SDK middleware and route or page wrappers establish the session and redirect unauthenticated visitors to login. An audience question locates these components in an existing deployment: this example protects an agent embedded in the web application. The larger code change also checks for the federated account connection, supplies OIDC client options, and enables the connect-account endpoint. Carlos attributes much of the apparent complexity to connected-account handling, which the team was working to simplify in the SDK.
The demonstration uses an immediate login redirect, although an application could provide a login button or another entry point. Signing in authenticates against the upstream identity provider. The signed-in assistant still presents a Connect Account action: having a session does not finish the delegation.
The application loads the identity-provider profile into the model’s context, so the assistant can now recognize its user. That identity is linked to the trading dashboard, but the assistant still lacks permission to read the user’s holdings. The fictional trading account displays a 10K balance and recent orders. Connecting the account prompts for additional scopes, even though the user has already signed in with the same credentials. This consent establishes the agent’s access to the account with the requested permissions. Knowing who someone is does not authorize reading what they own.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Keep token acquisition inside the tool implementation
The next tool reads the portfolio; creating orders remains unavailable at this stage. Its TypeScript implementation can be shared between a native agent tool and an MCP tool. Initially it runs locally and makes an ordinary HTTP request. The authorization work happens around that request: the SDK obtains an access token for the named upstream connection, then the tool uses it for a portfolio GET, optionally including history.
A small TypeScript adapter captures this separation. The server supplies the configured portfolio endpoint and a token getter bound to the authenticated user; the model-facing tool only needs to choose whether to include history.
typescript
type ConnectionTokenGetter = (connection: string) => Promise<string>;
export function createPortfolioReader(
portfolioEndpoint: URL,
connection: string,
getConnectionToken: ConnectionTokenGetter,
) {
return async function getPortfolio(includeHistory = false): Promise<unknown> {
const accessToken = await getConnectionToken(connection);
const url = new URL(portfolioEndpoint);
if (includeHistory) url.searchParams.set("includeHistory", "true");
const response = await fetch(url, {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (!response.ok) {
throw new Error(`Portfolio request failed: ${response.status}`);
}
return response.json();
};
}
The connection name tells the SDK which upstream relationship to use; it is not itself a credential. When Patrick asks the assistant to show his portfolio, the tool can now retrieve owned data. Carlos emphasizes that the tool implementer still determines the details of the access behavior.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
The upstream service defines what a scope means
The MCP server is modeled as an API with permissions for reading trades and portfolios, and those scopes appear in tool metadata. An audience member asks what makes a portfolio permission mean a stock portfolio rather than a project-management portfolio. The answer is the upstream service’s contract. A service requires particular scopes and rejects requests that lack them, whether the caller is an agent or a conventional REST client. The agent implementer must know both the request shape and the authorization requirements.
The connection configuration requests the upstream scopes. Those permissions appear in the user’s consent prompt, and Token Vault handles the exchange between the local and upstream contexts. The demonstration uses matching names for simplicity, but local and upstream scope names need not match. Nothing in this process requires the model to infer permission semantics from the word portfolio.
Scopes describe API access. Roles and personas describe another dimension of policy; Patrick points to role-based controls and FGA for restrictions around tools or pages. The connection mechanism itself already existed in Auth0—the new piece here is using a connection for Token Vault.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Expose the same tools through an authenticated MCP server
An audience question about discovering scopes leads into the MCP implementation. The server exposes a well-known protected-resource metadata endpoint that advertises supported scopes and authorization information. Middleware verifies token signatures and claims, then checks the permissions required by the protected handlers. This is signature verification and claim validation, rather than encryption of the JWT payload. Patrick describes the dynamic client registration feature as entering early access that week.
For calls onward to the trading API, the MCP server receives a linked custom API client, constructed with its own client ID and secret. Linking an API to a client gives the server a client identity for its upstream work; separate clients can represent independently managed components. The implementation reuses the stock and portfolio tools, registers them on the MCP server, and invokes the authorization middleware when constructing the transport endpoint.
The workshop implements both ends to make the entire path visible. An application could instead consume an existing upstream MCP server and implement only its client-side authorization. Here the agent and MCP endpoint run on the same Next.js server, but co-location does not remove their distinct authorization boundaries, and separate hosting can use the same arrangement.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
A prompt cannot grant a scope
Could a user inject a message telling the model that its connection now has a different scope? Carlos answers at the token boundary: scopes outside the configured connection would be ignored or rejected, depending on the upstream, rather than appearing in the issued access token. The authorization path is ordinary application code. When the tool needs to call an API, the SDK wrapper obtains its token before the protected call; the LLM does not directly run the authorization request. That limits the proposed scope-escalation attack, without proving that every action or argument available under an already granted permission is safe.
MCP Inspector then makes the authorization sequence visible against the local MCP endpoint. Open dynamic client registration is still early access in this demonstration, and the presenters raise concerns about its scaling characteristics before stepping through the flow:
- Fetch the MCP server’s protected-resource metadata and supported scopes.
- Retrieve the authorization server’s metadata, including how to authorize.
- Register a client for Inspector’s access to this server.
- Start the authorization-code flow with Proof Key for Code Exchange, or PKCE.
- Complete user authorization and exchange the resulting code for an access token.
- Connect to the MCP server with that token and access the portfolio and identity tools.
The tools still reach the same upstream stock API. Changing the client from the embedded chatbot to Inspector changes the entry point, not the user’s upstream account. Patrick also has a deployed instance ready for the later external-client demonstration.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Pause before placing the order
Reading a portfolio is not permission to let a hallucinating agent buy stocks in the middle of the night. The application developer decides that placing either a buy or sell order is risky, then adds a create-order tool with a mandatory backchannel authorization step. The request includes the stock symbol, quantity, and price. The user sees those details on a separate device, and only approval permits the order-placement path to continue.
The notification channel is Guardian, Auth0’s MFA application; a Guardian SDK supports applications that need their own interface. The user must enroll in MFA for this channel to reach them. Enrollment could occur during signup or as a later step-up interaction. For the workshop, the presenters use emailed enrollment instructions, then add a helper that forwards the authorization request to the SDK.
The helper wraps the SDK call and its errors. Underneath, the operation waits for the user’s response so the agent can resume afterward.
The order tool supplies structured authorization details through the custom API client. The example waits for verification; Patrick also mentions polling and other ways to organize the asynchronous flow. The essential control-flow boundary can be expressed in TypeScript without tying the tool to a particular SDK version:
typescript
type Order = Readonly<{
side: "buy" | "sell";
symbol: string;
quantity: number;
price: number;
}>;
type OrderAuthorization = {
requestApproval(order: Order): Promise<string>;
submitOrder(order: Order, accessToken: string): Promise<unknown>;
};
export function createApprovedOrderTool(auth: OrderAuthorization) {
return async function createOrder(input: Order): Promise<unknown> {
const order = Object.freeze({ ...input });
const accessToken = await auth.requestApproval(order);
return auth.submitOrder(order, accessToken);
};
}
The authorization adapter returns a token only after approval and rejects if authorization fails. The same order details pass to both approval and submission, and submission remains unreachable while approval is pending.
The live request uses fictional Wayne stock. Before requesting authorization, the agent tells the user that a push notification is coming. That message is a system-prompt-driven heads-up, not the security decision itself. After reconfirming that a final-state code branch exists, the presenters attempt the notification flow, which stalls while Patrick troubleshoots connectivity. Carlos notes email support alongside push notifications and describes more channels as future work.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Keep the user and the acting client identifiable
An audience member separates two kinds of preauthorization: deciding who may use an agent and deciding which services the agent may access. The first remains part of the application’s session and access-policy design. During the exchange, Patrick reports that reconnecting to the network delivered the notification and that he approved it. That establishes the reported approval event; the walkthrough does not clearly show a completed trade result.
The question then moves beyond chat to an unattended task runner. Carlos proposes attaching the user’s subject identifier to a task stored in the application’s database. His example is a task to buy Wayne stock when its price falls below a chosen threshold. The task retains its association with the user, and that subject identifies the connected upstream account. This is his suggested application design: a stored task carries identity forward after the interactive session ends, while the existing account connection supplies the delegation.
A follow-up asks how to distinguish an agent’s actions from the person’s own actions, especially if an agent goes rogue. The agent is an OAuth client, so the token can identify both the human subject and the application receiving delegated access. In Auth0’s access-token profiles, that client identifier is azp in the Auth0 profile or client_id in the RFC 9068 profile; aud identifies the intended resource recipient. These identifiers distinguish clients, not necessarily individual runs of an agent. Policy can use the combination of user and client to decide which delegation is acceptable.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Carry the approved transaction to the resource server
Patrick opens the logs to distinguish the Async Auth CIBA exchange from the earlier portfolio-token exchange, then examines a token payload. Rich Authorization Requests, or RAR, provide structured authorization_details for expressing what the user is consenting to beyond a simple scope string. Those details are recorded in the access token. The resource server must enforce the relevant approved details against the operation it receives; the consuming application stack owns that check. Approval only constrains the eventual action if the API respects what was approved.
The phone notification appears after connectivity is restored, when Patrick receives a backlog of requests. Rendering the transaction introduces another engineering problem: authorization details can contain application-specific structures, but the user needs an intelligible approval screen. Auth0 supplies a schema that Guardian can render dynamically and that custom applications can also use. It connects the structured request to a readable presentation of the details the person is being asked to approve.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Deploy the protected tools and connect another client
The final integration step deploys the application to Vercel, with Upstash Redis handling MCP server state. Patrick describes the integrations as very recent and beta. Hosting the agent and MCP server makes the same protected tools available to clients outside the original web application.
The deployed instance uses the same identity provider, so authenticating to it should recover the same user information. Patrick briefly shows the ChatGPT/OpenAI Apps SDK integration material rather than performing that setup live; he says extra configuration is still needed. The main live path returns to Inspector, obtains access to the deployed server, and then moves into Claude Code. Carlos stresses that external clients should encounter the same authentication and authorization policies.
The final README supplies a Claude Code configuration command. Patrick initially reuses Inspector’s access token because he reports an open issue with specifying scopes in Claude’s authentication flow. The presenters describe inconsistent client implementations of the then-new specification. The first attempt reports a connection but does not expose the expected tools; restarts and retries do not immediately fix it.
While that connection is being debugged, an audience member asks whether an organization using an Okta workspace tenant can use these Auth0 capabilities. Carlos tentatively describes a bridge that could apply employee policies to agents, but explicitly says he is unsure of the details. Okta and Auth0 remain separate products, and he offers to follow up with documentation. Back at the terminal, Carlos suggests running the Claude configuration command outside the active Claude session.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Delegation must outlast an access token
Why create an agent client at all instead of handing it the user’s access token? Carlos focuses on the operational problem: that token expires. An autonomous agent would then need to interrupt its work and bring the user back through login. Token Vault maintains the connected-account relationship and takes care of token storage, refreshing, and scope step-up. Patrick reports that the Claude connection is working while this discussion continues.
For a task runner, Carlos imagines a dashboard where a user connects Slack, Gmail, or Google Calendar. Later sessions reuse those connections without repeating every account-linking prompt and consent screen. That is the durable part of delegation: a continuing authorized relationship, rather than one copied access token. It is still subject to upstream policy. Refresh tokens can expire, and when the relationship can no longer supply valid credentials, the application must ask the user to reconnect.
Patrick then confirms that he completed authentication and can access the same tools in the deployed instance from Claude Code. He offers command syntax or his Claude version as possible explanations for the earlier trouble, without establishing a definitive cause. The closing registration options include DCR, static clients, and preconfigured clients; client ID metadata is described as a coming option. The later MCP authorization revision of November 25, 2025 includes client ID metadata documents and makes DCR optional, but that specification update does not establish when particular clients or Auth0 deployments implemented it.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Continue from the completed workshop
The final audience request is practical: where is the code with all the demonstrated changes? Patrick opens the completed branch, visible as finished-state in the demo-trade-pro-public repository. He says it contains the applied changes, including an adjustment to an order-history tool and additional tools beyond those covered in the walkthrough.
Patrick promises to link the branch in the notes and likely push it to the upstream workshop repository. Carlos closes with an operational warning: the hosted workshop application can experience disruptions while development continues. The code handoff is the starting point for extending the example; the shared demonstration service is still changing.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Resources
From the talk
Connected-account credentials and token exchanges for calling external APIs on a user’s behalf.
Auth0’s CIBA and RAR approval flow, including Guardian and email notification configuration.
The OpenID specification for client-initiated authentication through a separate user interaction channel.
RFC 9396 defines structured authorization details for permissions beyond simple scope strings.
Further reading
Build a fictional stock-trading agent and MCP service with delegated access and human approval.
TypeScript examples for discovery metadata, token verification, scope checks and upstream token exchange.
A small authenticated MCP server with identity tools and MCP Inspector testing instructions.
Updates since the talk
- MCP authorization: November 2025 revisionDocumentation
Versioned authorization requirements covering discovery, client ID metadata documents, preregistration and DCR.
Read the transcript and explore slides
Loading transcript and slides…