Stop Giving Your AI Agents API Keys - Kim Maida, Keycard & Mason Egger, Temporal

Read the talk

How to authorize each agent tool call and recover a run after a crash

An expense agent needs to distinguish the employee who filed a claim from the agent that approved it. Kim Maida and Mason Egger use OAuth token exchange to preserve that distinction across services, then show how a Temporal worker can resume settlement without repeating a completed debit or saving its credentials.

From a talk by Kim Maida and Mason Egger

At a glance

Ideas worth remembering

  • Place token exchange in the code that dispatches each tool call. Request the scope for the proposed operation and exchange again when the MCP server crosses into the downstream API.

  • Test identity with the same request before and after sign-in. Inspect who filed the expense, who approved it and whether a filer within their monetary limit can approve their own claim.

  • Kill a settlement worker after the debit completion appears in history. Verify that restarting it reaches settlement using the recorded debit result.

  • Fetch credentials inside the executing activity, then inspect the recovered run’s history for both the result needed for replay and any credential material that slipped into a payload.

An API key approved the expense. Who clicked Approve?

Kim Maida filed a $75 expense for workshop snacks, then clicked Approve on somebody else’s expense. Both operations succeeded. When she opened the activity log, the most it could tell her about either decision was that an API key had granted access. The people in the room were running separate agents against the same expense service. The ledger had no reliable way to identify which person or agent had submitted a claim, much less tell whether somebody had approved their own.

The Approve button illustrates why adding a person to the loop only gets you so far. Clicking it sent a prompt to the agent, which used the permissions available through its key. The person made a decision, but their identity disappeared on the way to the ledger. At the same time, the expense desk had a legitimate use for an autonomous approver. An agent could handle inexpensive office supplies under its own authority and leave larger claims for a human reviewer. Those two paths require the application to know whose authority it is exercising on each call.

1:26
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

1:26 · section reference included

Exchange the user’s token at each resource boundary

Maida, who works at Keycard, used OAuth token exchange to carry a user’s identity through the Expense Desk application. The user signs in and consents to delegate a subset of their permissions, expressed as scopes. The authorization server issues a token identifying that user and their level of access. This becomes the subject token when the application requests a credential to act on the user’s behalf. A separate credential identifies the application making the request.

The point to request access is when the model has proposed a tool call. In Expense Desk, a LangChain agent runs inside a custom application. Its Model Context Protocol (MCP) client dispatches calls to an expense MCP server, which exposes the tools and talks to the ledger API. Before dispatch, the application authenticates to Keycard’s security token service, presents the subject token and requests access to the MCP server for the proposed operation. The service can now evaluate the agent, the user, the target resource and the requested scope together.

If policy permits the call, the service issues a bearer token scoped to that operation and addressed to the MCP server. The server validates it. To reach the ledger, the server authenticates as an actor in its own right and exchanges the incoming token for a credential addressed to the ledger API. The ledger validates that credential, and the MCP server discards it after use. Maida said these tokens typically expire within minutes. The extra exchange on the second hop gives the ledger its own authorization boundary while preserving the identity of the user who initiated the work.

1:59
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

1:59 · section reference included

Give the agent an identity it can use on its own

In Keycard’s configuration, a component can have two registrations. An application can request tokens. A resource can receive tokens addressed to it. Expense Desk needed both because it would accept a user’s sign-in token and then request access downstream. Maida linked the resource to the application that provides it. Omitting that association would cause Keycard to reject the exchange of a sign-in token addressed to the resource. The expense MCP server likewise had both roles because it serves the agent and calls the ledger.

In the sandbox, Maida supplied a client ID and secret so the agent could authenticate. She suggested workload identity for a production deployment. The LLM provider’s API key lived in a vault, keeping that separate secret off the agent’s disk. In the application code, the MCP toolset used the Keycard SDK to request a scoped token for each call. When the minting function received no user subject token, it used a client credentials grant, giving the application a way to act under its own identity.

Maida tested that distinction by asking the agent to file $75 for workshop swag on her behalf before she had signed in. The agent put her name in the memo, just as she had asked. The activity record attributed the filing to the agent. Filtering the credential log by the MCP resource showed the client credentials grant. On the next hop, the log identified the MCP server as the requester accessing the ledger on behalf of that agent. The memo recorded what someone typed. The grants established which actors took part.

12:51
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

12:51 · section reference included

Separate the filer, the delegated agent and the approver

After Maida signed in, Expense Desk could read the $100 approval limit associated with her identity. The agent retained its own authority to approve eligible expenses under $50. When she filed $30 for office supplies, the application used her delegated authority to create the expense. The agent then used its own authority to approve it. The activity view showed her email on the creation event and the agent’s identifier on the approval, along with the reason the purchase qualified.

A $75 filing exercised two different checks. It exceeded the agent’s autonomous approval limit, so it stayed pending. Maida then asked the agent to approve it. Although $75 fell within her personal limit, the expense was hers and the application required another reviewer. She could reject a different pending expense that had been created under the agent’s identity. Earlier entries created with API keys still showed only those keys. Adding identities improved attribution from that point forward.

The business activity and the credential log answered different questions about the same work. The former showed who filed, approved or rejected an expense. In the latter, Maida could find her agent application requesting an MCP token on her behalf, then find the MCP server requesting ledger access on her behalf. Both exchanges carried the same session number. That gave her a way to follow a particular operation across a server shared by everyone in the workshop.

31:22
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

31:22 · section reference included

Recover settlement from the last completed activity

Mason Egger of Temporal introduced a second failure mode. Suppose the ledger debit succeeds and the worker dies before it marks the expense settled. Starting settlement again risks repeating the debit. Farther back in an agent loop, starting over could also mean paying for model calls whose answers the application already received. To resume safely at the point shown in the workshop, a replacement worker needs a durable record that the debit completed and what it returned.

Egger put the sequence of steps in a Temporal workflow and the external operations in activities. The settlement workflow set a retry policy, called the debit activity, waited on a timer that stood in for a human review delay, and then called the activity that marked the expense settled. Worker processes executed the code. A separate Temporal service maintained the execution history, including completed activity results, so the history survived the loss of a worker.

Egger killed the worker after the service had recorded the debit result. The workflow task timed out while the worker was down, and the completed debit remained visible in the history. After restart, Temporal reconstructed the workflow’s state using the recorded responses and proceeded to mark the expense settled. The timing matters. The experiment showed recovery after a recorded completion. It demonstrated that replay could reuse that debit result, the same property that lets an agent reuse a completed model activity’s response.

41:26
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

41:26 · section reference included

Keep the result for replay and fetch the credential at execution

A history detailed enough to reconstruct a run can also preserve sensitive inputs and outputs. Egger opened the settlement history and could read the results of the debit and settlement activities. He then ran a Python script against that workflow ID to check for JWT material. It found none in the demonstrated run. A worker could therefore recover the information it needed about the debit without recovering the bearer token used to authorize it.

Keycard’s integration used a Temporal interceptor, a hook that runs when the worker executes an activity. A decorator identified the resource the activity needed, and the interceptor obtained the credential at execution time. That kept the token out of the workflow history altogether. For business data that does need to survive replay but should stay confidential, Egger described a separate option: a payload encoder can encrypt the data before it reaches the Temporal service, with the application operator controlling the keys. In the demonstration, the activity results were readable and the credentials stayed outside the recorded payloads.

49:28
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

49:28 · section reference included