Concepts

Who is acting, for whom, and who said they could?

An API that receives a request from an AI agent needs three answers: which software is calling, whose authority it carries, and what that authority covers. This page explains the parties involved, the ways authority is handed on, and the claims in a token that carry the answers.

ReviewedNovember 2026

Review details
This page
ExplainerBackground for the flow guides
Review
Checked against sources in November 2026How it was checked

The roles

Five kinds of party. Every diagram on this site uses the same names and the same colors for them.

RoleWhat it holdsWhat it verifiesAppears as
UserResource ownerThe authority to grant: their own data and permissions.Proves who they are to the authorization server, never to the agent.End user, employee, approver
AgentClientA client identity of its own, and whatever tokens are issued to it.Which authorization server it is talking to, and that responses come from it.Agent, MCP client, tool server in the middle
Authorization serverIssuerSigning keys, and the record of who approved what.The user, the client, and whether policy allows the request. Then issues a token.Authorization server, identity provider, SPIRE server
Resource serverThe API being calledNothing of the user’s or the agent’s. It only receives tokens.Each token’s issuer, audience, scope and expiry, and proof of possession where required.API, MCP server, remote agent, website
InfrastructureThe platform underneathKnowledge of which workload is actually running, and where.A workload’s identity, which it can attest to an authorization server.Kubernetes, gateway, egress proxy

The principal is whichever party’s authority a request exercises: usually the user, sometimes the agent itself. The actor is the software exercising it. Most of what goes wrong with agents comes from a resource server at the end of a chain that cannot tell the two apart, or knows neither.

What a request should be able to answer

Nine questions, and where in a token or a request each answer is carried.

QuestionWhere the answer lives
Who is acting
What software is acting?client_id, the act claim, and the workload identity of the process making the call.
Which agent, exactly?Often nothing. Most tokens name the application that hosts an agent, not the agent. Several current drafts are trying to fix that.
Does it matter who holds the token?A bearer token can be used by anyone holding a copy. A token with a cnf claim additionally requires proof of the key it names.
For whom
Whose authority is this?The subject of the token: sub. A person, or the agent itself.
Was it meant for this resource?The audience, aud, and whether the first two answers survive all the way to the last hop.
Who said they could
On what grounds?A user’s consent, an administrator’s policy, or a signed mandate. Something outside the agent said yes.
What is it for?scope, authorization_details, and transaction context.
When does it stop?exp, and whether anyone can revoke it sooner.
How fresh is it?iat, auth_time and acr: when and how strongly the person last proved who they are.

The rest of this site is about the mechanisms that carry those answers from where they are known to where they are needed.

Why agents are different

Agents use the same protocols as every other program. What changes is how hard they lean on parts of those protocols that used to be edge cases.

Delegation is the normal case

A web app mostly acts as itself and occasionally for a user. An agent exists to act for someone else. The distinction between “the user did this” and “software did this for the user” stops being academic.

Connections are made at runtime

Classic OAuth assumes a developer registered the client with each API ahead of time. An agent may be pointed at a server nobody involved has seen before, and must discover how to authenticate on the spot.

Calls fan out

One request becomes an agent calling tools that call services that call other agents. Authority has to follow the chain without growing along the way, and without any hop holding more than it needs.

The caller can be talked into things

An agent reads untrusted text and acts on it. A credential in its reach is a credential an injected instruction can misuse or leak. Controls that live in the prompt are suggestions; controls at the resource are enforcement.

Work outlives the session

A person approves something and closes the laptop. The agent keeps going for hours. Either it holds a long-lived grant, or it needs a way to reach the person later.

Identity has two layers

There is the workload, a process running somewhere, and there is the principal it is working for. They are proven by different credentials, and a sound design carries both.

What the API can verify

One user, one agent, one API. The credential the agent presents decides what the API can establish.

What the API can verifyThe agent presents: its own api keyThe agent presents: a token alice delegated
Who is actingWhich software the credential was issued toYesThe key maps to the account it was issued to. That identifies the agent only if the agent has a key of its own.Yesclient_id names the client the token was issued to. That is the agent’s registration, which may stand for many running copies of it.
For whomWhich user the request is forNoNothing the API can verify names Alice. A user ID added by the agent is only the agent’s word.Yessub names Alice. Where a resource owner is involved, the profile says sub should identify them, and the authorization server that signed the token vouches for it.
Who said they couldWhat was granted for that userNoThe key carries the access an administrator gave the agent, the same whichever user it is working for. Nothing in the request records a grant for Alice.Yesscope and aud record what the authorization server granted after Alice approved, and where it applies. The API sees that record, not the approval itself, and it covers a scope, not each action. The profile only recommends scope, so a token may carry none.
ProofThat the sender is the party it was issued toNoA key is a bearer secret. It works for anyone who has a copy.ConditionalOnly for a sender-constrained token. A bearer token can be used by anyone holding a copy. A sender-constrained token additionally requires proof of the key it is bound to, with DPoP or mutual TLS. That proof shows the sender holds the key. It does not establish which software is running.
So the API canThe API can limit or refuse the agent as a whole. It cannot tell one user’s request from another’s. When the agent acts as itselfThe API can use both identifiers in its access policy: for example, blocking this client while still allowing Alice to use another. How the agent gets this token

How the API verifies

API key
The API looks the key up in its own records and finds the account it was issued to. Nothing is read from the key itself.
Access token
The API checks the token’s signature against the issuer’s published keys, and that the issuer, audience and expiry are the ones it expects. An API that cannot read the token itself can ask the authorization server about it instead (token introspection).

Every verdict in the table assumes this has been done. An unvalidated token proves nothing.

About the example token

The token in this example is a JWT access token following the JWT profile for OAuth 2.0 access tokens (RFC 9068). That profile requires sub and client_id, and recommends scope when the request asked for one.

It is abbreviated. A complete token under the profile also carries iss, exp, aud, iat and jti, and is signed.

Not every access token is a JWT, and not every JWT follows this profile. An API that asks the authorization server instead, by token introspection (RFC 7662), gets an answer in which sub, client_id and scope are all optional.

What a valid credential does not prove

  • That the sender is the software named. Both are bearer credentials: anyone holding a copy can use them. A sender-constrained token additionally requires proof of the key it is bound to, with DPoP or mutual TLS; that is the fourth check above. Even then, the proof shows possession of a key. It does not establish which software is running.
  • That Alice approved this particular request. The token records a scope granted earlier. A request inside that scope looks the same whether or not she would agree to it now.
  • That Alice is present, or has not changed her mind. The token is good until it expires, or until the API learns it was revoked.
  • Which running copy of the agent sent it. client_id names a registration. Many instances, or many agents hosted by one application, can share it.

Refusing and revoking

The API’s own access policy
API key: The API can refuse the key. Every user the agent serves is refused together, because nothing in a request tells them apart.
Access token: The API can use both identifiers: for example, block this client while still allowing Alice to use another, or the reverse. That takes a validated token and a policy that reads both sub and client_id. Carrying two identifiers does not do it by itself.
Revocation
API key: Whoever issued the key can revoke it, with the same effect.
Access token: A client asks the authorization server to revoke a token (RFC 7009). That is the authorization server’s mechanism, separate from the API’s policy.
How the API learns of a revocation
API key: The API looks the key up on every request, so it sees a revoked key at once.
Access token: Local token validation alone does not reveal a later revocation. Without an additional revocation mechanism, the API may continue accepting the token until it expires. Introspection is one such mechanism. RFC 7009 also describes communication between the authorization server and the API, and short-lived tokens that limit the exposure.

Delegation or impersonation

Add a tool server between the agent and the API. The API is now the last hop of a chain: what does it know? Pick how authority is passed down and see.

How is authority passed down the chain?
What the downstream API can tell
Who is the user?No
What software is calling?Partly
Was this credential meant for me?Yes
Is a stolen copy useless elsewhere?No
Can one user or one agent be cut off?No

The API sees one caller: whoever holds the key. Every user and every agent run looks the same, the key works from anywhere, and revoking it cuts off everyone at once. Still the most common setup in the wild.

What the downstream API can tell
Who is the user?Yes
What software is calling?No
Was this credential meant for me?No
Is a stolen copy useless elsewhere?No
Can one user or one agent be cut off?Partly

The user is visible, but the API is accepting a token that was issued for a different service. Every hop that touched the token can replay it wherever it is accepted. This is the confused-deputy setup, and the MCP specification prohibits it.

What the downstream API can tell
Who is the user?Yes
What software is calling?No
Was this credential meant for me?Yes
Is a stolen copy useless elsewhere?Partly
Can one user or one agent be cut off?Partly

Each hop gets a fresh token for the right audience. But the API believes the user is calling directly. Logs show Alice doing things at 3 a.m. that Alice never did, and policy cannot treat software differently from a person.

Token exchange →
What the downstream API can tell
Who is the user?Yes
What software is calling?Yes
Was this credential meant for me?Yes
Is a stolen copy useless elsewhere?Partly
Can one user or one agent be cut off?Yes

The token names both the user and the chain of software acting for them. The API can apply the user’s permissions and the agent’s limits together, and either can be revoked on its own. A stolen token is still usable until it expires, but only at this one API.

Token exchange →
What the downstream API can tell
Who is the user?Yes
What software is calling?Yes
Was this credential meant for me?Yes
Is a stolen copy useless elsewhere?Yes
Can one user or one agent be cut off?Yes

As above, and each token is bound to a key held by the caller. Copying a token out of a log or a model’s context gets an attacker nothing. This is where the standards are heading; few deployments are here yet.

Sender-constrained tokens →

Impersonation means the downstream service cannot distinguish the software from the person. It is simple, and it is how most integrations work today. Delegation keeps both identities: the token says who it is for and, separately, who is acting. The standard mechanism is the act claim from token exchange.

The confused deputy problem is what happens without it. A service with broad authority is tricked by a caller into using that authority on the caller’s behalf. An agent that forwards tokens, or that serves many users with one identity, is a deputy waiting to be confused. Audience-restricted tokens and explicit delegation are the defenses.

With and without a human

Once it is settled whose authority the agent is using, the next question is whether that person is available when access is needed.

The trap in the middle

The dangerous configuration is the one that is neither: an unattended agent holding a person’s long-lived token. It has a human’s full authority and no human watching. If you find yourself here, narrow the scope, bind the token to a key, make the person reachable for anything irreversible, and know how you would revoke it.

What is in a token

Nine credentials you will meet. Open any claim to see which question it answers.

User access token

Authorization server → agent → API

What an agent holds after a person signs in and consents. The JWT profile (RFC 9068) is one common format; opaque tokens checked by introspection carry the same information.

{
"iss": "https://auth.example",
Bookkeeping

The authorization server that issued the token. The API must check it against the issuer it trusts; an agent that talks to many servers must check it against the one it meant to use.

"sub": "user:alice",
Who is this for?

The subject: whose authority this token carries. Here, a human. APIs apply this user’s permissions.

"client_id": "https://agent.example/client.json",
What software is acting?

The OAuth client the token was issued to. This is usually the closest thing to “which agent” in a plain access token, and it identifies an application, not an individual agent run. With Client ID Metadata Documents the value is a URL the server can fetch to learn about the client.

"aud": "https://calendar.example/api",
Where is it valid?

The audience: the one API that should accept this token. Agents talk to many servers, so audience restriction (requested with the resource parameter) is what stops a token given to one server from being replayed at another.

"scope": "calendar.read",
What may it do?

What the user consented to. Coarse by design. For anything transaction-shaped, authorization_details (RFC 9396) is more precise.

"exp": 1794269400,
For how long?

Expiry. Minutes, not days. A leaked short-lived token is a small incident.

"iat": 1794268800,
For how long?

When the token was issued.

"jti": "8f1c9a52-…"
Bookkeeping

A unique identifier for this token, used for replay detection, revocation lists and correlating audit logs.

}

Delegated token

Authorization server → tool → downstream API

The result of a token exchange. The user is still the subject; the act claim names who is acting for them. Nested act claims record the chain.

{
"iss": "https://auth.example",
Bookkeeping

Issuer.

"sub": "user:alice",
Who is this for?

Still Alice. Delegation keeps the original subject so that downstream permissions are hers.

"aud": "https://warehouse.example/api",
Where is it valid?

A new audience. The token the tool received was addressed to the tool; this one is addressed to the next hop.

"scope": "sales.read",
What may it do?

Narrower than the original grant. Each exchange is a chance to drop privileges the next hop does not need.

"exp": 1794269100,
For how long?

Shorter-lived than the token it was exchanged for.

"act": {
What software is acting?

The actor claim from RFC 8693. Its presence is the difference between delegation (“the tool, acting for Alice”) and impersonation (“Alice”). Without it the API cannot tell software from the person.

"sub": "https://tools.example/mcp",
What software is acting?

The current actor: the party presenting this token.

"act": { "sub": "research-agent" }
What software is acting?

A prior actor, nested. RFC 8693 says only the outermost actor is relevant to access control; nested actors are history, useful for audit. Several current drafts would make this chain richer for agents.

}
}

Agent’s own token

Authorization server → agent → API

Issued by the client credentials grant. There is no user. The agent is the subject, and everything it does with this token is on its own authority.

{
"iss": "https://auth.example",
Bookkeeping

Issuer.

"sub": "invoice-agent",
Who is this for?

The subject is the client itself. An API must not treat this as a user; RFC 9068 warns about exactly this confusion and recommends that authorization servers keep client and user identifiers from colliding.

"client_id": "invoice-agent",
What software is acting?

Same value as sub: the token was issued to the party it describes.

"aud": "https://erp.example/api",
Where is it valid?

One API.

"scope": "invoices.read invoices.reconcile",
What may it do?

Granted by an administrator ahead of time. Nobody was asked at issuance.

"exp": 1794269400
For how long?

No refresh token accompanies it. The agent simply requests another.

}

Kubernetes ServiceAccount token

Cluster API server → kubelet → pod → external verifier

A projected, bound token: issued for one pod, with a chosen audience and a short life. It is a workload identity document, and the usual starting point for federation.

{
"iss": "https://oidc.cluster.example",
Bookkeeping

The cluster’s service account issuer. External systems fetch /.well-known/openid-configuration from here to find the signing keys.

"sub": "system:serviceaccount:agents:research-agent",
Who is this for?

Namespace and service account name. This is the string that federation trust rules match on, so the mapping from service accounts to agents is your identity model.

"aud": ["https://sts.example"],
Where is it valid?

Set per projected volume. Give each consumer its own audience. A token whose audience is the Kubernetes API server should never be sent anywhere else.

"exp": 1794272400,
For how long?

The kubelet refreshes the file on disk before this passes.

"iat": 1794268800,
For how long?

Issued at.

"jti": "5d2f0c0e-…",
Bookkeeping

Unique token ID, recorded in API server audit logs so a token’s use can be traced back to its issuance.

"kubernetes.io": {
How is possession proven?

Binding information. The token is valid only while the objects named here exist. Delete the pod and the token is dead, whatever exp says.

"namespace": "agents",
Who is this for?

The namespace, repeated in structured form.

"serviceaccount": { "name": "research-agent", "uid": "…" },
Who is this for?

Name and UID. The UID changes if the service account is deleted and recreated, which invalidates old tokens.

"pod": { "name": "research-agent-7c9f-x2x8k", "uid": "…" },
What software is acting?

The specific pod. This is the nearest thing Kubernetes offers to “this running instance of the agent.”

"node": { "name": "node-3", "uid": "…" }
What software is acting?

The node the pod is scheduled on.

}
}

JWT-SVID

SPIRE → workload → peer workload

A SPIFFE identity in JWT form, for cases where a certificate cannot reach the application. Deliberately minimal.

{
"sub": "spiffe://example.org/ns/agents/sa/research-agent",
Who is this for?

The SPIFFE ID: a trust domain (example.org) and a path that the operator chooses. Here the path mirrors the Kubernetes namespace and service account, a common convention.

"aud": ["spiffe://example.org/ns/tools/sa/search"],
Where is it valid?

Required. The workload asks for a token for a specific audience, and the receiver must reject tokens addressed to anyone else.

"exp": 1794269100,
For how long?

Required and short. JWT-SVIDs are bearer tokens.

"iat": 1794268800
For how long?

Issued at.

}

DPoP proof

Agent → authorization server or API, on every request

Not a token that grants anything. A one-time signed statement that the sender holds the private key an access token is bound to.

// header
{
"typ": "dpop+jwt",
Bookkeeping

Marks this JWT as a DPoP proof so it cannot be confused with any other kind.

"alg": "ES256",
How is possession proven?

An asymmetric signature algorithm. Symmetric algorithms and none are not allowed.

"jwk": { "kty": "EC", "crv": "P-256", "x": "…", "y": "…" }
How is possession proven?

The public key. The proof is signed with the matching private key. The server computes this key’s thumbprint and compares it with cnf.jkt in the access token.

}
// payload
{
"jti": "e1j3V_bKic8-LAEB",
Bookkeeping

Unique per proof. Servers remember recent values and reject repeats.

"htm": "GET",
Where is it valid?

The HTTP method this proof is for.

"htu": "https://calendar.example/api/events",
Where is it valid?

The URL this proof is for, without query or fragment. A proof captured for one endpoint is useless at another.

"iat": 1794268830,
For how long?

Creation time. Servers accept only a narrow window.

"ath": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo"
How is possession proven?

Hash of the access token being presented, tying this proof to that token.

}

ID-JAG

Enterprise IdP → agent → the application’s authorization server

An Identity Assertion JWT Authorization Grant. Not an access token: a short-lived statement from the company identity provider that this client may get one, for this user, at that application.

// header
{ "typ": "oauth-id-jag+jwt" }
Bookkeeping

An explicit type, so the grant cannot be mistaken for an ID token or an access token and replayed as one.

// payload
{
"iss": "https://acme.idp.example",
Bookkeeping

The enterprise identity provider. The application’s authorization server trusts it because the company’s tenant is configured for single sign-on with it.

"sub": "U019488227",
Who is this for?

The employee, as the identity provider knows them. The receiving side maps this to its own account.

"aud": "https://auth.chat.example/",
Where is it valid?

The authorization server of the target application, not the application’s API. Only that server should accept this grant.

"resource": "https://mcp.chat.example/",
Where is it valid?

The API, here an MCP server, that the eventual access token must be restricted to.

"client_id": "f53f191f9311af35",
What software is acting?

The client the grant was issued to, as known to the receiving authorization server. The client presenting the grant must match.

"scope": "chat.read chat.history",
What may it do?

What the administrator’s policy allowed, which may be less than was requested.

"jti": "9e43f81b64a33f20116179",
Bookkeeping

Single-use identifier.

"exp": 1311281970,
For how long?

Minutes. The grant exists only to be exchanged immediately.

"iat": 1311280970
For how long?

Issued at.

}

Transaction token

Transaction token service → every workload in one call chain

Issued at the edge of a trust domain in exchange for the external access token, then passed unchanged from service to service.

// header
{ "typ": "txntoken+jwt" }
Bookkeeping

Explicitly typed. Carried in the Txn-Token HTTP header, not Authorization.

// payload
{
"aud": "trust-domain.example",
Where is it valid?

The trust domain itself. Every workload inside may accept it; nothing outside should.

"txn": "97053963-771d-49cc-a4e3-20aad399c312",
Bookkeeping

A unique transaction identifier, the same at every hop. This is what ties the logs of a dozen services to one user request.

"sub": "d084sdrt234fsaw34tr23t",
Who is this for?

The user or workload the transaction is for.

"req_wl": "apigateway.trust-domain.example",
What software is acting?

The workload that requested this token, usually the gateway that received the external call.

"scope": "trade.stocks",
What may it do?

The purpose of the transaction. Narrower than the external token’s scope.

"rctx": { "req_ip": "69.151.72.123", "authn": "face" },
How is possession proven?

Request context: the circumstances of the original call, such as the caller’s address and how the user authenticated.

"tctx": { "action": "BUY", "ticker": "MSFT", "quantity": "100" },
What may it do?

Transaction context: the fixed parameters of what was asked for. Signed, so no service downstream, and no agent talked into something else, can change them.

"iat": 1686536226,
For how long?

Issued at.

"exp": 1686536586
For how long?

Minutes. Long enough for one call chain to finish.

}

Workload Identity Token

Identity issuer → workload → any peer, always with a proof

The WIMSE working group’s workload credential. It names a workload and carries its public key. It is never valid alone.

// header
{ "alg": "ES256", "typ": "wit+jwt" }
Bookkeeping

Typed. Carried in the Workload-Identity-Token header. A receiver must refuse it if it arrives as a bearer token.

// payload
{
"iss": "https://issuer.example.org",
Bookkeeping

Recommended for auditing. Trust comes from the pre-configured binding between a trust domain and its issuer keys, not from following this value.

"sub": "spiffe://example.org/ns/agents/sa/research-agent",
Who is this for?

The workload identifier: a URI scoped to a trust domain. A SPIFFE ID qualifies. For an agent, this is “what is acting”; a user’s delegation travels separately.

"cnf": { "jwk": { "kty": "OKP", "crv": "Ed25519", "x": "1CXX…" } },
How is possession proven?

The workload’s public key. Each request must come with a proof signed by the matching private key: a Workload Proof Token or an HTTP Message Signature.

"jti": "x-_1CTL2cca3CSE4cwb_l",
Bookkeeping

Token identifier.

"exp": 1745512510
For how long?

Short-lived and re-issued automatically. Note there is no audience: the same token is shown to every peer, which is safe only because of the key binding.

}
  • Who is this for?
  • What software is acting?
  • Where is it valid?
  • What may it do?
  • How is possession proven?
  • For how long?

Rules of thumb

  1. One audience per token. A token should work at exactly one resource. Request it that way with the resource parameter and reject anything addressed to someone else.
  2. Exchange, never forward. A service that needs to call another gets a new token for that hop. Passing along the one it received is how a token meant for one place ends up everywhere.
  3. Keep credentials out of the model’s context. Anything the model can read, an injected instruction can ask it to repeat. Tokens belong in the runtime around the model, or outside the sandbox entirely.
  4. Enforce at the resource. The agent asking for confirmation is a courtesy. The API refusing without a valid, specific authorization is a control.
  5. Prove the workload and the principal separately. Which process is calling, and whose authority it carries, are different questions with different credentials.
  6. Prefer identity you do not have to store. A platform-issued token that rotates itself beats a secret in an environment variable every time.
  7. Short lives, narrow scopes, and a way to say stop. Assume a token will leak and an agent will misbehave. Decide in advance how small the damage is and how fast you can end it.

See the flows →Or answer six questions