Skip to content
Start with Identity
Analysis

Giving AI agents identities: stop lending them yours

Most AI agents in production today run on a borrowed identity: the developer's session or a shared cloud role. October's Copilot CLI and AgentCore research shows what that costs. The fix is an identity per agent, scoped to its task, with the human carried in the token through RFC 8693 delegation.

By SWI Community TeamOct 23, 2026

An AI agent should have its own identity, scoped to the job it was given, and when it acts for a person the token it carries should name both of them. Most agents running today have neither. They run inside a developer's session, with every file and credential that person can reach, or under a shared cloud role whose permissions cover far more than one agent's task. October's research on GitHub Copilot CLI and Amazon Bedrock AgentCore showed what happens next: whoever can put text in front of the agent gets to use that identity. OAuth already has the delegation pattern this needs. It is called token exchange, RFC 8693, and it is underused.

Key takeaways

  • An agent running on a borrowed identity turns prompt injection into credential access, because the injected instruction inherits everything the identity can do.
  • Give each agent its own principal. A shared default role makes every agent's permissions available to every agent's users.
  • When an agent acts for a user, use delegation, not impersonation: RFC 8693's act claim keeps the agent visible in the token and in your logs.
  • Scope delegated tokens to the task and keep them short-lived. Token scope is the ceiling on what an injected instruction can do.
  • The token does not protect anything the agent can reach without it, such as files on disk or the instance metadata service.

Two ways October's agents were borrowing identities

The developer's session. Adversa AI showed Copilot CLI in autopilot mode fetching a web page with encrypted instructions, reading a local .env.prod, and sending its contents to an attacker URL in about 28 seconds with no further user interaction. One model in Copilot's lineup completed the chain in half of runs; two others refused. GitHub validated the finding but does not treat it as a vulnerability, because the user asked the agent to fetch untrusted content with full autonomy. That reasoning is accurate, and it is the point: the agent was the developer, as far as the file system and the network were concerned.

A shared execution role. Zenity Labs showed that an AgentCore agent could be prompted to fetch its execution role credentials from 169.254.169.254. The default role was scoped to every AgentCore resource in the account's region, including InvokeAgentRuntime, ListEvents (other agents' conversations), CreateEvent on memory, and secretsmanager:GetSecretValue. AWS moved AgentCore to IMDSv2-only on February 14, 2026, and Zenity observed the default role narrowed by September 29. AWS describes agent access to its role credentials as documented and expected, and recommends least-privilege execution roles. Again accurate. The default was the problem.

Neither case needed a model jailbreak in the dramatic sense. Both needed an identity that was larger than the task.

Three ways to give an agent access

Pattern What the agent holds Whose name is in the log What an injected instruction can reach
Borrowed session The user's own session, files and tokens The user's Everything the user can reach
Shared service role One role for many agents The role's Everything any agent on that role can reach
Delegated token A short-lived token naming user and agent Both Only the scopes granted for this task

The first two are what most teams ship because they are already there. The third takes design work, and it is the only one where your audit trail can answer "which agent did this, and for whom."

What delegation looks like on the wire

RFC 8693, OAuth 2.0 Token Exchange, separates two ideas in section 1.1. In impersonation, "A is B" within the rights the token grants, and downstream services cannot tell them apart. In delegation, the actor keeps its own identity and acts on the subject's behalf. For agents, you want delegation.

The agent sends the authorization server two tokens: the user's token as the subject_token, and its own credential as the actor_token. If policy allows, it gets back a new token whose subject is the user and whose act claim (section 4.1) names the agent:

{
  "sub": "user:priya@example.com",
  "aud": "https://api.example.com/tickets",
  "scope": "tickets:read tickets:comment",
  "exp": 1792765200,
  "act": { "sub": "agent:support-triage-07" }
}

The user's token can carry a may_act claim (section 4.4) saying which actors are allowed to do this at all, so an agent cannot mint delegated tokens for users who never authorized it. When agents call agents, act claims nest to record the chain. The RFC says that for access control, only the top-level claims and the current actor count; earlier actors in the chain are informational. Build your policy with that in mind: if you want to block a particular upstream agent, you have to check at the hop where it was the current actor.

Three design rules make this work in practice:

  1. Every agent gets its own client identity, registered like any other non-human identity, with an owner and a purpose. No pooled credentials.
  2. Scopes come from the task, not the agent. A triage agent that comments on tickets gets tickets:comment for this user, for minutes. Our scope escalation in delegation chains brief covers what goes wrong when a middle hop forwards broader scopes than it received.
  3. Resource servers log act. If the API only records sub, the agent disappears from the audit trail and you are back to impersonation in everything but name.

What a token cannot fix

Delegation narrows what an agent can do through APIs. It does nothing about what the agent can reach without asking. Copilot CLI read .env.prod from disk; no authorization server was involved. AgentCore credentials came from the metadata endpoint, not from an OAuth flow. So the token design has to sit next to two environmental controls:

  • Run agents where the secrets are not. No production .env files on machines where coding agents run, IMDSv2 enforced or metadata blocked entirely for agent runtimes, and credentials fetched from a secrets manager per task instead of sitting in the environment.
  • Separate fetching from acting. An agent that reads untrusted web content in the same session where it can make outbound requests with your data is the Copilot CLI chain. Keep autopilot away from arbitrary URLs, or split the two capabilities across agents with different identities.

The same lesson shows up in trusted publishing: in SlugFest, npm matched a binding by repository name until its first successful publish, so a reclaimed name could publish. GitHub's fix expires unvalidated configurations 48 hours after creation; matching by name still applies inside that window. An identity is only as good as the identifier it binds to, whether that is a repository slug or an agent's client ID.

Before you ship the next agent

  • Does it have its own identity, distinct from the developer who built it and from every other agent?
  • Who owns that identity, and what happens to it when the agent is retired?
  • When it acts for a user, does the token carry act, and does the user's token limit who may act?
  • Are its scopes set per task, with lifetimes in minutes?
  • What can it reach without a token: files, metadata endpoints, environment variables?
  • Can it read untrusted content and send data out in the same session?
  • Will your logs show which agent did something, not just which user?
Last reviewed By SWI Community TeamSuggest a correctionHow we research
Independent analysis. No vendor sponsorship. Editorial policy · Report a correction