# When an agent calls APIs for a user, use delegation, not a shared service account

> Giving the agent an admin account gets the demo working this afternoon. When something goes wrong, though, that shortcut can't answer the first question the security team will ask.

Original: https://fdetimes.net/en/guides/agent-on-behalf-of-delegation-oauth/

It is your second week on site, and the internal support agent you are building can already read tickets and update orders. To make Friday's demo, one of the customer's engineers suggests giving the agent a service account with admin rights on the ticketing system.

The suggestion sounds entirely reasonable. It is also the point at which many FDEs take on a security debt that is very hard to pay back later.

The problem is the logs. Three months on, if a ticket is closed by mistake or customer data is read without authorisation, the security team will ask who did it. With a shared credential, you cannot answer.

Auth0 describes exactly this situation when discussing static API keys shared by agents: there is no way to know which user, or which of the agent's actions, used the key.

According to Auth0, that lack of an audit trail is unacceptable for security, compliance and incident response. If you want your agent to get through a security review at a large enterprise, you need to have the skills in this article down cold.

## Should the agent become the user, or act alongside them?

RFC 8693 defines OAuth 2.0 Token Exchange, a protocol for requesting security tokens from an authorization server. It supports both impersonation and delegation. The two sound alike but leave very different logs.

On impersonation, the RFC says that when A impersonates B, A is B within the rights of the token. The downstream API sees only B, and any trace of the party actually acting disappears. With delegation, A keeps its own identity, separate from B. An agent acting for a user should choose the second.

The tool for recording delegation is the `act` (actor) claim in a JWT. RFC 8693 created this claim to state explicitly that a token was issued through delegation, and who the acting party is.

**Key point:** An agent should act on behalf of the user, but never become the user.

## A complete flow, step by step

Picture a support agent named Lan asking the agent to summarise the open tickets. The chat application sends the agent Lan's token, whose audience is the agent itself. The agent does not take that token and call the ticketing system with it. Instead it sends a token exchange request to the authorization server, with a `grant_type` value defined by RFC 8693:

```http
POST /token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=<token Lan sent to the agent>
&actor_token=<the agent's own identity token>
&scope=tickets.read
```

If the request is accepted, the new token's payload will look roughly like this. Lan remains the subject, the agent sits in `act`, and the scope grants read access only:

```json
{
  "sub": "lan@customer.example",
  "scope": "tickets.read",
  "act": { "sub": "support-agent" }
}
```

As a result, every line in the ticketing system's log answers two questions: who asked, and who acted. That is precisely what a shared service account cannot do.

## On-behalf-of: three constraints every FDE should know by heart

If the customer runs Microsoft Entra, the concrete version of this idea is the On-Behalf-Of (OBO) flow. Microsoft describes OBO as OAuth delegation, used to carry the user's identity and permissions through a chain of requests. Its documentation sets out three constraints FDEs should remember.

First, OBO uses only delegated scopes, not application roles. The middle tier therefore cannot give a user more permissions than they already have. If Lan cannot delete tickets, an agent acting for Lan cannot delete them either, even if a prompt injection tries to trick it into doing so.

Second, an application may not exchange a token that was issued to a different application. The incoming token's audience must be the agent itself. A token issued for another API must be rejected, not forwarded.

The third is the one most often missed. When a downstream API requires Conditional Access, such as MFA, the middle tier cannot satisfy it on its own. It must return the error to the client so the client can take the user through the authentication step.

This is also the clearest reason a service identity cannot stand in for a user: a service has no phone to approve an MFA prompt.

## Three common mistakes on customer sites

The first is token passthrough: accepting a token from the client and passing it unchanged to a downstream API. The Model Context Protocol security guidance calls this an anti-pattern, and the authorization specification forbids it outright.

The reason is that the downstream API's logs will record the request as if it came from a different source with a different identity, not from the server that actually forwarded it.

The second is requesting every scope up front "so we don't have to ask again". The MCP documentation warns that poorly designed scopes increase the damage when a token leaks, frustrate users and blur the audit trail.

For a summarisation task, request only `tickets.read`, and ask for write access only when the user actually asks for an update.

The third is letting the agent hold each provider's refresh tokens itself. Auth0's Token Vault pattern addresses this: the agent holds only Auth0 tokens and exchanges them for provider tokens when needed, never touching the provider's refresh tokens.

If the customer's identity provider already supports something similar, use it rather than building your own token store.

## Do it yourself on the next project

Start by drawing the call chain: user, client, agent, then each downstream API. On every arrow, write down which token is travelling and what its audience is. The drawing usually shows at once where someone intends to forward a token or share a key.

Next, in the agent, check the audience of every incoming token before doing anything else. Exchange tokens with token exchange or OBO, request the narrowest scope that covers the task, and return Conditional Access errors to the client intact rather than swallowing them and retrying with a different credential.

Finally, sit down with the customer's security team and read a real log line from the downstream API to see whether it shows both the user and the agent.

For your career, this is a skill that is easy to demonstrate. When reading FDE job descriptions, look for mentions of OAuth, identity or integration with enterprise systems. If they are there, put this experience at the top.

On your CV, don't just write "experience with OAuth". Describe how you designed a delegation flow that made every API call by the agent traceable to the right user.

A sentence like that tells an interviewer you have built systems that can pass a real security review.

This week's exercise: take an agent you have written, decode the token it uses to call downstream APIs, and ask yourself whether, if an incident happened tomorrow, that token would tell you who asked the agent to act. If the answer is no, you know what to fix before your next demo.

**Try this week:**

- Pick an agent or integration you are working on, list every credential it uses and mark the ones shared across multiple users.
- Decode an access token your agent receives and check that its audience really is the agent. Then write a token exchange request against your identity provider's test environment.
- Add a line to your CV or portfolio describing how you designed things so that every API call by the agent can be traced back to a user.

## Sources

- [RFC 8693 - OAuth 2.0 Token Exchange](https://datatracker.ietf.org/doc/html/rfc8693)

- [RFC 8693 - OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html)

- [Microsoft identity platform and OAuth2.0 On-Behalf-Of flow - Microsoft identity platform | Microsoft Learn](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-on-behalf-of-flow)

- [Security Best Practices - Model Context Protocol](https://modelcontextprotocol.io/specification/draft/basic/security_best_practices)

- [API Keys and AI Agents: Four Common Risks](https://auth0.com/blog/api-key-security-for-ai-agents/)

- [Auth0 Token Vault: Secure Token Exchange for AI Agents](https://auth0.com/blog/auth0-token-vault-secure-token-exchange-for-ai-agents/)
