<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Agent identity · Brennenstuhl on Security</title><link>https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/</link><description>How AI agents get scoped, revocable access to tools and services when they act on behalf of people.</description><language>en-GB</language><copyright>Original writing CC BY 4.0, Jan Brennenstuhl</copyright><atom:link href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/index.xml" rel="self" type="application/rss+xml"/><item><title>Should an agent use a robot account or delegated access?</title><link>https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/</link><guid isPermaLink="true">https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/</guid><pubDate>Tue, 13 Oct 2026 00:00:00 +0200</pubDate><description>&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; When an agent acts for a person, give it delegated access that names both the person and the agent, with narrow scopes, an expiry, and a way to revoke it. A robot account shared by several people gives the agent their combined permissions and hides whose request was whose.&lt;/p&gt;&lt;h2 id="key-findings"&gt;Key findings &lt;a class="heading-anchor" href="#key-findings" aria-label="Link to section: Key findings"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure class="finding" id="f-agent-robot-account-combined-permissions"&gt;
&lt;blockquote&gt;&lt;p&gt;When several people share an agent that holds a robot account, the agent acts with the combined permissions of all its users, and nobody can tell whose request was whose.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.janbrennenstuhl.eu/agentic-identity-broker/" class="ext"&gt;&lt;cite&gt;What I Learned Building an Identity Broker With Coding Agents (janbrennenstuhl.eu, 2026)&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-robot-account-combined-permissions" title="#f-agent-robot-account-combined-permissions"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="When several people share an agent that holds a robot account, the agent acts with the combined permissions of all its users, and nobody can tell whose request was whose." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/#f-agent-robot-account-combined-permissions" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/f-agent-robot-account-combined-permissions.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-delegation-keeps-actor-identity"&gt;
&lt;blockquote&gt;&lt;p&gt;Under the delegation semantics of OAuth 2.0 Token Exchange, the acting party keeps its own identity, separate from the person whose rights it uses.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.rfc-editor.org/rfc/rfc8693#section-1.1" class="ext"&gt;&lt;cite&gt;RFC 8693, OAuth 2.0 Token Exchange (IETF, 2020), Section 1.1&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-delegation-keeps-actor-identity" title="#f-agent-delegation-keeps-actor-identity"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="Under the delegation semantics of OAuth 2.0 Token Exchange, the acting party keeps its own identity, separate from the person whose rights it uses." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/#f-agent-delegation-keeps-actor-identity" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/f-agent-delegation-keeps-actor-identity.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-token-least-privilege"&gt;
&lt;blockquote&gt;&lt;p&gt;The OAuth 2.0 security best current practice says an access token&amp;rsquo;s privileges should be restricted to the minimum the use case requires, and the token should be audience-restricted to a specific resource server.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.rfc-editor.org/rfc/rfc9700#section-2.3" class="ext"&gt;&lt;cite&gt;RFC 9700, Best Current Practice for OAuth 2.0 Security (IETF, 2025), Section 2.3&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-token-least-privilege" title="#f-agent-token-least-privilege"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="The OAuth 2.0 security best current practice says an access token&amp;rsquo;s privileges should be restricted to the minimum the use case requires, and the token should be audience-restricted to a specific resource server." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/#f-agent-token-least-privilege" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/f-agent-token-least-privilege.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-no-long-lived-keys"&gt;
&lt;blockquote&gt;&lt;p&gt;Coding agents should not hold persistent identity and access management (IAM) roles or long-lived API keys; an identity broker can issue short-lived, task-scoped credentials on demand instead.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.janbrennenstuhl.eu/appsec-agentic-engineering/" class="ext"&gt;&lt;cite&gt;AppSec in the Age of Agentic Engineering (janbrennenstuhl.eu, 2026)&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-no-long-lived-keys" title="#f-agent-no-long-lived-keys"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="Coding agents should not hold persistent identity and access management (IAM) roles or long-lived API keys; an identity broker can issue short-lived, task-scoped credentials on demand instead." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/#f-agent-no-long-lived-keys" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/f-agent-no-long-lived-keys.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="evidence"&gt;Evidence &lt;a class="heading-anchor" href="#evidence" aria-label="Link to section: Evidence"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="table-scroll studies" tabindex="0" role="region" aria-label="Primary sources on robot accounts and delegated agent access"&gt;&lt;table&gt;
&lt;caption&gt;Primary sources on robot accounts and delegated agent access&lt;/caption&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th scope="col"&gt;Study&lt;/th&gt;&lt;th scope="col"&gt;Year&lt;/th&gt;&lt;th scope="col"&gt;Sample&lt;/th&gt;&lt;th scope="col"&gt;Method&lt;/th&gt;&lt;th scope="col"&gt;Finding&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc8693" class="ext"&gt;OAuth 2.0 Token Exchange (RFC 8693)&lt;/a&gt;&lt;span class="org"&gt;IETF&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2020&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Defines delegation and impersonation semantics and the act claim that names the acting party.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9700" class="ext"&gt;Best Current Practice for OAuth 2.0 Security (RFC 9700)&lt;/a&gt;&lt;span class="org"&gt;IETF&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2025&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Asks for least-privilege, audience-restricted tokens and warns when resource servers can&amp;rsquo;t tell client tokens from user tokens.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization" class="ext"&gt;Model Context Protocol: Authorization (revision 2026-07-28)&lt;/a&gt;&lt;span class="org"&gt;Model Context Protocol&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2026&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Treats clients acting for a user differently from clients acting on their own behalf.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.janbrennenstuhl.eu/agentic-identity-broker/" class="ext"&gt;What I Learned Building an Identity Broker With Coding Agents&lt;/a&gt;&lt;span class="org"&gt;this site&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2026&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Field report&lt;/td&gt;&lt;td&gt;Explains why the Agentic Identity Broker keeps both the user and the agent in every authorization decision.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.janbrennenstuhl.eu/appsec-agentic-engineering/" class="ext"&gt;AppSec in the Age of Agentic Engineering&lt;/a&gt;&lt;span class="org"&gt;this site&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2026&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Essay&lt;/td&gt;&lt;td&gt;Argues for short-lived, task-scoped credentials and user-revocable delegation for agents.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id="what-to-do"&gt;What to do &lt;a class="heading-anchor" href="#what-to-do" aria-label="Link to section: What to do"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="for-engineers"&gt;For engineers &lt;a class="heading-anchor" href="#for-engineers" aria-label="Link to section: For engineers"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ask first whom the agent acts for. If it runs a job on its own behalf, a dedicated client with its own narrow permissions is a reasonable fit. The &lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/"&gt;MCP authorization specification&lt;/a&gt; explicitly mentions such &lt;code&gt;client_credentials&lt;/code&gt; clients. If it acts for a person, use a grant that names that person and that agent, and keep it narrow and short-lived as &lt;a href="https://www.rfc-editor.org/rfc/rfc9700#section-2.3" class="ext"&gt;RFC 9700&lt;/a&gt; asks.&lt;/p&gt;
&lt;p&gt;Prefer delegation over impersonation. A delegated token can carry the agent as the actor next to the person as the subject, so a resource server and your logs can tell them apart. RFC 9700 also warns that a resource server that can&amp;rsquo;t distinguish tokens obtained with a person&amp;rsquo;s involvement from tokens a client obtained on its own may let that client reach the person&amp;rsquo;s resources.&lt;/p&gt;
&lt;p&gt;Keep provider credentials out of the agent&amp;rsquo;s context. &lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/"&gt;Token exchange&lt;/a&gt; shows how a gateway can do this on each tool call.&lt;/p&gt;
&lt;h3 id="for-product-and-ux"&gt;For product and UX &lt;a class="heading-anchor" href="#for-product-and-ux" aria-label="Link to section: For product and UX"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Show people which agent holds access to which service, with what permissions and until when. Give them one place to &lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/"&gt;revoke that access&lt;/a&gt;. If several people use the same agent, show each of them only the grants they made.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How it works &lt;a class="heading-anchor" href="#how-it-works" aria-label="Link to section: How it works"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With a robot account, the agent holds a long-lived credential for each MCP server or API it calls. In OAuth terms, that is closest to a client that gets tokens for itself, without a person in the flow. RFC 9700 quotes the JSON Web Token (JWT) profile for access tokens (RFC 9068): the &lt;code&gt;sub&lt;/code&gt; claim of such a token should identify the client application, while a token obtained with a person&amp;rsquo;s involvement should identify that person. A robot account therefore can&amp;rsquo;t express who asked for an action. In &lt;a href="https://www.janbrennenstuhl.eu/agentic-identity-broker/"&gt;my retrospective&lt;/a&gt;, I describe the consequences once several people share the agent: combined permissions, no attribution, and credentials within reach of anything that tricks the agent.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc8693#section-1.1" class="ext"&gt;RFC 8693&lt;/a&gt; describes 2 ways one party can use another&amp;rsquo;s rights. With impersonation, party A gets B&amp;rsquo;s rights and is indistinguishable from B in that context. With delegation, A keeps its own identity, and it&amp;rsquo;s explicitly understood that A acts for B. The RFC uses this exact phrase: &amp;ldquo;In a sense, A is an agent for B.&amp;rdquo; A token that carries both identities is a composite token. In a JWT, the &lt;code&gt;act&lt;/code&gt; claim names the acting party.&lt;/p&gt;
&lt;p&gt;The Agentic Identity Broker that Magnus Jungsbluth and I built applies delegation. A user grants an agent access to a service with a chosen permission set and an optional expiry and can revoke the grant at any time. The broker keeps both the user and the agent in every authorization decision. The essay and our &lt;a href="https://www.janbrennenstuhl.eu/talks/economies-of-scale-for-mcp/"&gt;AGNTCon + MCPCon talk&lt;/a&gt; explain the design, and the &lt;a href="https://www.janbrennenstuhl.eu/projects/agentic-identity-broker/"&gt;project page&lt;/a&gt; links the code.&lt;/p&gt;
&lt;p&gt;This model covers agents that act for a person. For autonomous agents with no human in the loop, I wrote in the retrospective that every approach I know of is still brittle.&lt;/p&gt;
&lt;h2 id="related"&gt;Related &lt;a class="heading-anchor" href="#related" aria-label="Link to section: Related"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/"&gt;How does OAuth 2.0 Token Exchange keep provider tokens away from agents?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/"&gt;How should people grant and revoke an agent&amp;rsquo;s access?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/"&gt;How does authorization work for MCP servers?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/appsec-agentic-engineering/"&gt;AppSec in the Age of Agentic Engineering&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>How does OAuth 2.0 Token Exchange keep provider tokens away from agents?</title><link>https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/</link><guid isPermaLink="true">https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/</guid><pubDate>Tue, 13 Oct 2026 00:00:00 +0200</pubDate><description>&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; OAuth 2.0 Token Exchange (RFC 8693) lets a client trade one token for another token meant for a specific target service. When a gateway runs that exchange on each tool call, as in the Agentic Identity Broker, the provider token stays between the broker, the gateway and the provider and never reaches the agent.&lt;/p&gt;&lt;h2 id="key-findings"&gt;Key findings &lt;a class="heading-anchor" href="#key-findings" aria-label="Link to section: Key findings"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure class="finding" id="f-agent-rfc8693-security-token-service"&gt;
&lt;blockquote&gt;&lt;p&gt;RFC 8693 lets a client present a security token to an OAuth 2.0 authorization server acting as a Security Token Service and receive a new token, for example one scoped more narrowly for a downstream service.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.rfc-editor.org/rfc/rfc8693#section-1" class="ext"&gt;&lt;cite&gt;RFC 8693, OAuth 2.0 Token Exchange (IETF, 2020), Section 1&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-rfc8693-security-token-service" title="#f-agent-rfc8693-security-token-service"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="RFC 8693 lets a client present a security token to an OAuth 2.0 authorization server acting as a Security Token Service and receive a new token, for example one scoped more narrowly for a downstream service." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/#f-agent-rfc8693-security-token-service" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/f-agent-rfc8693-security-token-service.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-broker-provider-token-stays-out"&gt;
&lt;blockquote&gt;&lt;p&gt;In the Agentic Identity Broker, an MCP gateway exchanges the agent&amp;rsquo;s token for the matching provider token through RFC 8693 on each tool call, so the provider token never reaches the agent.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.janbrennenstuhl.eu/agentic-identity-broker/" class="ext"&gt;&lt;cite&gt;What I Learned Building an Identity Broker With Coding Agents (janbrennenstuhl.eu, 2026)&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-broker-provider-token-stays-out" title="#f-agent-broker-provider-token-stays-out"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="In the Agentic Identity Broker, an MCP gateway exchanges the agent&amp;rsquo;s token for the matching provider token through RFC 8693 on each tool call, so the provider token never reaches the agent." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/#f-agent-broker-provider-token-stays-out" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/f-agent-broker-provider-token-stays-out.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-exchange-client-authentication"&gt;
&lt;blockquote&gt;&lt;p&gt;RFC 8693 warns that without client authentication at the token endpoint, anyone holding a compromised token can trade it for other tokens.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.rfc-editor.org/rfc/rfc8693#section-2.1" class="ext"&gt;&lt;cite&gt;RFC 8693, OAuth 2.0 Token Exchange (IETF, 2020), Section 2.1&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-exchange-client-authentication" title="#f-agent-exchange-client-authentication"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="RFC 8693 warns that without client authentication at the token endpoint, anyone holding a compromised token can trade it for other tokens." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/#f-agent-exchange-client-authentication" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/f-agent-exchange-client-authentication.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-act-claim-current-actor"&gt;
&lt;blockquote&gt;&lt;p&gt;In a JWT issued through token exchange, access control decisions must consider only the top-level claims and the current actor named in the act claim, while nested prior actors are informational.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.rfc-editor.org/rfc/rfc8693#section-4.1" class="ext"&gt;&lt;cite&gt;RFC 8693, OAuth 2.0 Token Exchange (IETF, 2020), Section 4.1&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-act-claim-current-actor" title="#f-agent-act-claim-current-actor"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="In a JWT issued through token exchange, access control decisions must consider only the top-level claims and the current actor named in the act claim, while nested prior actors are informational." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/#f-agent-act-claim-current-actor" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/f-agent-act-claim-current-actor.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-exchange-no-revocation-link"&gt;
&lt;blockquote&gt;&lt;p&gt;A token exchange creates no tight link between the input and output tokens, so passing revocation from one to the other depends on the implementation and isn&amp;rsquo;t a property of the protocol.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.rfc-editor.org/rfc/rfc8693#section-2.1" class="ext"&gt;&lt;cite&gt;RFC 8693, OAuth 2.0 Token Exchange (IETF, 2020), Section 2.1&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-exchange-no-revocation-link" title="#f-agent-exchange-no-revocation-link"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="A token exchange creates no tight link between the input and output tokens, so passing revocation from one to the other depends on the implementation and isn&amp;rsquo;t a property of the protocol." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/#f-agent-exchange-no-revocation-link" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/f-agent-exchange-no-revocation-link.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="evidence"&gt;Evidence &lt;a class="heading-anchor" href="#evidence" aria-label="Link to section: Evidence"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="table-scroll studies" tabindex="0" role="region" aria-label="Primary sources on token exchange for agents"&gt;&lt;table&gt;
&lt;caption&gt;Primary sources on token exchange for agents&lt;/caption&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th scope="col"&gt;Study&lt;/th&gt;&lt;th scope="col"&gt;Year&lt;/th&gt;&lt;th scope="col"&gt;Sample&lt;/th&gt;&lt;th scope="col"&gt;Method&lt;/th&gt;&lt;th scope="col"&gt;Finding&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc8693" class="ext"&gt;OAuth 2.0 Token Exchange (RFC 8693)&lt;/a&gt;&lt;span class="org"&gt;IETF&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2020&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Defines the token exchange grant, its request and response parameters, and the act and may_act claims.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc8707" class="ext"&gt;Resource Indicators for OAuth 2.0 (RFC 8707)&lt;/a&gt;&lt;span class="org"&gt;IETF&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2020&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Defines the resource parameter that tells the authorization server where a token will be used.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.janbrennenstuhl.eu/agentic-identity-broker/" class="ext"&gt;What I Learned Building an Identity Broker With Coding Agents&lt;/a&gt;&lt;span class="org"&gt;this site&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2026&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Field report&lt;/td&gt;&lt;td&gt;Describes how a gateway exchanges the agent&amp;rsquo;s token for the matching provider token on each tool call.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.janbrennenstuhl.eu/talks/economies-of-scale-for-mcp/" class="ext"&gt;Economies of Scale for MCP and Agents&lt;/a&gt;&lt;span class="org"&gt;this site&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2026&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Talk&lt;/td&gt;&lt;td&gt;Presents the broker&amp;rsquo;s delegation model at AGNTCon + MCPCon Europe 2026.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id="what-to-do"&gt;What to do &lt;a class="heading-anchor" href="#what-to-do" aria-label="Link to section: What to do"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="for-engineers"&gt;For engineers &lt;a class="heading-anchor" href="#for-engineers" aria-label="Link to section: For engineers"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Put the exchange in a component the agent can&amp;rsquo;t control, such as a gateway, and authenticate that component at the token endpoint. Name the target with &lt;code&gt;resource&lt;/code&gt; or &lt;code&gt;audience&lt;/code&gt; and ask for the smallest &lt;code&gt;scope&lt;/code&gt; the tool call needs. RFC 8693 points out that broad requests across many target services are more likely to be refused.&lt;/p&gt;
&lt;p&gt;Treat revocation as your own job. The RFC leaves it to the implementation whether revoking a grant or an input token affects tokens already issued, so keep issued tokens short-lived and check the grant at exchange time. &lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/"&gt;Consent and revocation&lt;/a&gt; covers the rest.&lt;/p&gt;
&lt;p&gt;If an agent&amp;rsquo;s token can travel further, use the &lt;code&gt;act&lt;/code&gt; claim to record the agent as the actor and the &lt;code&gt;may_act&lt;/code&gt; claim to state who may act for the subject.&lt;/p&gt;
&lt;h3 id="for-product-and-ux"&gt;For product and UX &lt;a class="heading-anchor" href="#for-product-and-ux" aria-label="Link to section: For product and UX"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Users don&amp;rsquo;t see the exchange, but they see its effects. When a tool call fails because a grant expired or was revoked, tell the user which service and which agent were affected and how to grant access again.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How it works &lt;a class="heading-anchor" href="#how-it-works" aria-label="Link to section: How it works"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;RFC 8693 defines the exchange as 1 request and 1 response at an authorization server&amp;rsquo;s token endpoint. With a gateway between an agent and its tools, the exchange runs like this:&lt;/p&gt;
&lt;section class="explainer" data-explainer="delegation" aria-labelledby="ex-delegation"&gt;
&lt;h3 id="ex-delegation"&gt;Step through the exchange&lt;/h3&gt;
&lt;div class="explainer-static"&gt;&lt;ol&gt;
&lt;li&gt;The agent calls a tool through the gateway and presents its own token.&lt;/li&gt;
&lt;li&gt;The gateway acts as the RFC 8693 client. It sends a &lt;code&gt;POST&lt;/code&gt; to the token endpoint with &lt;code&gt;grant_type=urn:ietf:params:oauth:grant-type:token-exchange&lt;/code&gt;, a &lt;code&gt;subject_token&lt;/code&gt; that represents the person on whose behalf the request is made, and its &lt;code&gt;subject_token_type&lt;/code&gt;. Optional parameters name the target (&lt;code&gt;resource&lt;/code&gt;, &lt;code&gt;audience&lt;/code&gt;), the access wanted (&lt;code&gt;scope&lt;/code&gt;), and the token type wanted (&lt;code&gt;requested_token_type&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The gateway authenticates with the normal OAuth 2.0 client authentication methods.&lt;/li&gt;
&lt;li&gt;The authorization server validates the presented tokens and decides by policy whether to issue a token for the named target. If it refuses, it answers with &lt;code&gt;invalid_request&lt;/code&gt;, or with &lt;code&gt;invalid_target&lt;/code&gt; when it won&amp;rsquo;t issue a token for the requested target.&lt;/li&gt;
&lt;li&gt;A successful response is a normal OAuth token response: &lt;code&gt;access_token&lt;/code&gt; carries the issued token, together with &lt;code&gt;issued_token_type&lt;/code&gt;, &lt;code&gt;token_type&lt;/code&gt;, and, as recommended, &lt;code&gt;expires_in&lt;/code&gt;. The issued token doesn&amp;rsquo;t have to be an OAuth access token.&lt;/li&gt;
&lt;li&gt;The gateway calls the target service with the issued token and returns only the result to the agent.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="explainer-app" hidden&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;p&gt;The following request shows step 2 with placeholder hosts and tokens. It illustrates the RFC parameters and isn&amp;rsquo;t the broker&amp;rsquo;s API. I&amp;rsquo;ve left the values unencoded and added line breaks for readability; a real request URL-encodes the form body.&lt;/p&gt;
&lt;figure class="code" data-lang="http"&gt;&lt;figcaption class="code-file"&gt;&lt;span class="sr-only"&gt;File: &lt;/span&gt;token-exchange.http&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" aria-label="token-exchange.http" class="chroma"&gt;&lt;code data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/token&lt;/span&gt; &lt;span class="kr"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="l"&gt;broker.example.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Authorization&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="l"&gt;Basic &amp;lt;gateway client credentials&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Content-Type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="l"&gt;application/x-www-form-urlencoded&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;amp;subject_token=&amp;lt;token the agent presented&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;amp;subject_token_type=urn:ietf:params:oauth:token-type:access_token
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;amp;resource=https://api.provider.example/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;amp;scope=&amp;lt;permissions the tool call needs&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;The Agentic Identity Broker that Magnus Jungsbluth and I built uses this pattern, as I describe in the &lt;a href="https://www.janbrennenstuhl.eu/agentic-identity-broker/"&gt;retrospective&lt;/a&gt;. A user grants an agent access to a service, such as GitHub, with a chosen permission set and an optional expiry, and the broker stores the provider tokens encrypted. On each tool call, an MCP gateway such as agentgateway exchanges the agent&amp;rsquo;s token for the matching provider token, so the provider token never reaches the agent.&lt;/p&gt;
&lt;p&gt;RFC 8693 also defines an optional &lt;code&gt;actor_token&lt;/code&gt; for the party that will act on behalf of the subject. In the issued JWT, the &lt;code&gt;act&lt;/code&gt; claim can record that actor, and nested &lt;code&gt;act&lt;/code&gt; claims record a chain of earlier actors. The &lt;a href="https://github.com/zalando-incubator/agentic-identity-broker#roadmap" title="Agentic Identity Broker roadmap" class="ext"&gt;broker roadmap&lt;/a&gt; lists token exchange for agent-to-agent calls as planned work.&lt;/p&gt;
&lt;h2 id="related"&gt;Related &lt;a class="heading-anchor" href="#related" aria-label="Link to section: Related"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/"&gt;Should an agent use a robot account or delegated access?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/"&gt;How does authorization work for MCP servers?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/talks/economies-of-scale-for-mcp/"&gt;Economies of Scale for MCP and Agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/projects/agentic-identity-broker/"&gt;Agentic Identity Broker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>How should people grant and revoke an agent's access?</title><link>https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/</link><guid isPermaLink="true">https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/</guid><pubDate>Tue, 13 Oct 2026 00:00:00 +0200</pubDate><description>&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; Let people grant each agent access per service, with a permission set they choose and an expiry, and let them revoke that grant at any time. Revoking the grant has to stop every token derived from it, so keep issued tokens short-lived and check the grant whenever a new token is issued.&lt;/p&gt;&lt;h2 id="key-findings"&gt;Key findings &lt;a class="heading-anchor" href="#key-findings" aria-label="Link to section: Key findings"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure class="finding" id="f-agent-grant-scope-expiry-revoke"&gt;
&lt;blockquote&gt;&lt;p&gt;In the Agentic Identity Broker, a user grants an agent access to a service with a chosen permission set and an optional expiry, and can revoke that grant at any time.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.janbrennenstuhl.eu/agentic-identity-broker/" class="ext"&gt;&lt;cite&gt;What I Learned Building an Identity Broker With Coding Agents (janbrennenstuhl.eu, 2026)&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-grant-scope-expiry-revoke" title="#f-agent-grant-scope-expiry-revoke"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="In the Agentic Identity Broker, a user grants an agent access to a service with a chosen permission set and an optional expiry, and can revoke that grant at any time." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/#f-agent-grant-scope-expiry-revoke" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/f-agent-grant-scope-expiry-revoke.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-revocation-cascades-to-grant"&gt;
&lt;blockquote&gt;&lt;p&gt;Under RFC 7009, revoking a refresh token should also invalidate all access tokens based on the same authorization grant if the authorization server supports access token revocation.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.rfc-editor.org/rfc/rfc7009#section-2.1" class="ext"&gt;&lt;cite&gt;RFC 7009, OAuth 2.0 Token Revocation (IETF, 2013), Section 2.1&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-revocation-cascades-to-grant" title="#f-agent-revocation-cascades-to-grant"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="Under RFC 7009, revoking a refresh token should also invalidate all access tokens based on the same authorization grant if the authorization server supports access token revocation." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/#f-agent-revocation-cascades-to-grant" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/f-agent-revocation-cascades-to-grant.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-self-contained-token-revocation"&gt;
&lt;blockquote&gt;&lt;p&gt;Immediate revocation of self-contained access tokens needs backend coordination that RFC 7009 calls non-standardized, and short-lived access tokens limit how long a revoked token stays in use.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.rfc-editor.org/rfc/rfc7009#section-3" class="ext"&gt;&lt;cite&gt;RFC 7009, OAuth 2.0 Token Revocation (IETF, 2013), Section 3&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-self-contained-token-revocation" title="#f-agent-self-contained-token-revocation"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="Immediate revocation of self-contained access tokens needs backend coordination that RFC 7009 calls non-standardized, and short-lived access tokens limit how long a revoked token stays in use." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/#f-agent-self-contained-token-revocation" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/f-agent-self-contained-token-revocation.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-refresh-token-bound-to-consent"&gt;
&lt;blockquote&gt;&lt;p&gt;Refresh tokens must be bound to the scope and resource servers the resource owner consented to.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://www.rfc-editor.org/rfc/rfc9700#section-4.14.2" class="ext"&gt;&lt;cite&gt;RFC 9700, Best Current Practice for OAuth 2.0 Security (IETF, 2025), Section 4.14.2&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-refresh-token-bound-to-consent" title="#f-agent-refresh-token-bound-to-consent"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="Refresh tokens must be bound to the scope and resource servers the resource owner consented to." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/#f-agent-refresh-token-bound-to-consent" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/f-agent-refresh-token-bound-to-consent.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="evidence"&gt;Evidence &lt;a class="heading-anchor" href="#evidence" aria-label="Link to section: Evidence"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="table-scroll studies" tabindex="0" role="region" aria-label="Primary sources on agent consent and revocation"&gt;&lt;table&gt;
&lt;caption&gt;Primary sources on agent consent and revocation&lt;/caption&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th scope="col"&gt;Study&lt;/th&gt;&lt;th scope="col"&gt;Year&lt;/th&gt;&lt;th scope="col"&gt;Sample&lt;/th&gt;&lt;th scope="col"&gt;Method&lt;/th&gt;&lt;th scope="col"&gt;Finding&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc7009" class="ext"&gt;OAuth 2.0 Token Revocation (RFC 7009)&lt;/a&gt;&lt;span class="org"&gt;IETF&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2013&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Defines a revocation endpoint and how revoking one token can revoke related tokens and the grant.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9700" class="ext"&gt;Best Current Practice for OAuth 2.0 Security (RFC 9700)&lt;/a&gt;&lt;span class="org"&gt;IETF&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2025&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Binds refresh tokens to the consented scope and resources and lets them expire after inactivity.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc8707" class="ext"&gt;Resource Indicators for OAuth 2.0 (RFC 8707)&lt;/a&gt;&lt;span class="org"&gt;IETF&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2020&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Lets the authorization server tell the user which resources a client will access on their behalf.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc8693" class="ext"&gt;OAuth 2.0 Token Exchange (RFC 8693)&lt;/a&gt;&lt;span class="org"&gt;IETF&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2020&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Leaves revocation of tokens issued by an exchange to the implementation.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization" class="ext"&gt;Model Context Protocol: Authorization (revision 2026-07-28)&lt;/a&gt;&lt;span class="org"&gt;Model Context Protocol&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2026&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Asks clients to request minimal scopes first and add scopes through step-up authorization.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.janbrennenstuhl.eu/agentic-identity-broker/" class="ext"&gt;What I Learned Building an Identity Broker With Coding Agents&lt;/a&gt;&lt;span class="org"&gt;this site&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2026&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Field report&lt;/td&gt;&lt;td&gt;Describes per-agent grants with chosen permissions, optional expiry and revocation at any time.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id="what-to-do"&gt;What to do &lt;a class="heading-anchor" href="#what-to-do" aria-label="Link to section: What to do"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="for-engineers"&gt;For engineers &lt;a class="heading-anchor" href="#for-engineers" aria-label="Link to section: For engineers"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Model the grant as a record of its own that names the person, the agent, the service, the permissions and the expiry, and issue every token from it. Check that record whenever you issue or exchange a token, so a revocation takes effect at the next tool call. Expiring tokens are the fallback for tokens you can&amp;rsquo;t recall.&lt;/p&gt;
&lt;p&gt;Wire revocation through every layer you control. Call the &lt;a href="https://www.rfc-editor.org/rfc/rfc7009" class="ext"&gt;RFC 7009&lt;/a&gt; revocation endpoint where the provider offers one, and don&amp;rsquo;t stop at the first token. RFC 8693 says tokens issued by a &lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/"&gt;token exchange&lt;/a&gt; aren&amp;rsquo;t linked to their input token, so revoking one doesn&amp;rsquo;t reach the other unless you build that.&lt;/p&gt;
&lt;p&gt;Ask for the least access first. The &lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/"&gt;MCP authorization specification&lt;/a&gt; describes this as step-up authorization: request minimal scopes and add more when a server asks for them.&lt;/p&gt;
&lt;h3 id="for-product-and-ux"&gt;For product and UX &lt;a class="heading-anchor" href="#for-product-and-ux" aria-label="Link to section: For product and UX"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Show each grant with the agent, the service, the permissions and the expiry in plain words, and put the revoke action next to it. Default to an expiry instead of open-ended access. When an agent needs more access, ask at that moment for that specific permission.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How it works &lt;a class="heading-anchor" href="#how-it-works" aria-label="Link to section: How it works"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Granting access in OAuth starts with an authorization request. The client names the access it wants in &lt;code&gt;scope&lt;/code&gt; and, with &lt;a href="https://www.rfc-editor.org/rfc/rfc8707#section-2.1" class="ext"&gt;RFC 8707&lt;/a&gt;, the resources in &lt;code&gt;resource&lt;/code&gt;. RFC 8707 notes that the authorization server can use those resource values to inform the user about the resources the client will access on their behalf. The consent screen can then name the service, not only a list of scopes.&lt;/p&gt;
&lt;p&gt;In the Agentic Identity Broker, which Magnus Jungsbluth and I built, a user grants an agent access to a service with a chosen permission set and an optional expiry. The broker keeps both the user and the agent in every authorization decision. I describe the design in the &lt;a href="https://www.janbrennenstuhl.eu/agentic-identity-broker/"&gt;retrospective&lt;/a&gt;, and my &lt;a href="https://www.janbrennenstuhl.eu/appsec-agentic-engineering/"&gt;AppSec essay&lt;/a&gt; argues for narrow, time-limited permissions that the user can revoke at any moment.&lt;/p&gt;
&lt;p&gt;Revoking access in OAuth uses the revocation endpoint from RFC 7009. A client sends the token it wants revoked, and the server invalidates it and, depending on policy, related tokens and the grant itself. RFC 7009 gives 2 reasons this matters for people: it stops a grant from staying valid without the user&amp;rsquo;s knowledge, and revoked grants disappear from any list of grants the authorization server shows the user. The server answers with HTTP 200 even for an invalid token. A 503 means the client must assume the token still exists.&lt;/p&gt;
&lt;p&gt;Self-contained tokens make revocation harder. A resource server can check a self-contained token, such as a JSON Web Token (JWT), without asking the authorization server, so it won&amp;rsquo;t learn about a revocation on its own. RFC 7009 names short-lived access tokens with refresh tokens as one alternative. RFC 9700 adds that refresh tokens must stay bound to the consented scope and resources, should expire after a period of inactivity, and may be revoked on security events such as a password change or logout.&lt;/p&gt;
&lt;h2 id="related"&gt;Related &lt;a class="heading-anchor" href="#related" aria-label="Link to section: Related"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/"&gt;Should an agent use a robot account or delegated access?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/"&gt;How does OAuth 2.0 Token Exchange keep provider tokens away from agents?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/talks/economies-of-scale-for-mcp/"&gt;Economies of Scale for MCP and Agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/projects/agentic-identity-broker/"&gt;Agentic Identity Broker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>How does authorization work for MCP servers?</title><link>https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/</link><guid isPermaLink="true">https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/</guid><pubDate>Tue, 13 Oct 2026 00:00:00 +0200</pubDate><description>&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; In the MCP authorization specification (revision 2026-07-28), a protected MCP server over HTTP is an OAuth 2.1 resource server: it publishes Protected Resource Metadata that points clients to its authorization server and accepts only tokens issued for itself. MCP clients discover that authorization server, run the authorization code flow with PKCE, and name the MCP server in a resource parameter on every authorization and token request.&lt;/p&gt;&lt;p&gt;This entry describes the Model Context Protocol (MCP) &lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization" class="ext"&gt;authorization specification&lt;/a&gt; as of revision 2026-07-28, which the MCP versioning page listed as the current revision on 13 October 2026. Later revisions may change these requirements.&lt;/p&gt;
&lt;h2 id="key-findings"&gt;Key findings &lt;a class="heading-anchor" href="#key-findings" aria-label="Link to section: Key findings"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure class="finding" id="f-agent-mcp-protected-resource-metadata"&gt;
&lt;blockquote&gt;&lt;p&gt;As of MCP revision 2026-07-28, MCP servers must implement OAuth 2.0 Protected Resource Metadata (RFC 9728), and MCP clients must use it to discover the authorization server.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization" class="ext"&gt;&lt;cite&gt;Model Context Protocol, Authorization, revision 2026-07-28&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-mcp-protected-resource-metadata" title="#f-agent-mcp-protected-resource-metadata"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="As of MCP revision 2026-07-28, MCP servers must implement OAuth 2.0 Protected Resource Metadata (RFC 9728), and MCP clients must use it to discover the authorization server." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/#f-agent-mcp-protected-resource-metadata" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/f-agent-mcp-protected-resource-metadata.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-mcp-resource-parameter"&gt;
&lt;blockquote&gt;&lt;p&gt;MCP clients must send the RFC 8707 resource parameter with the MCP server&amp;rsquo;s canonical URI in both authorization and token requests, whether or not the authorization server supports it.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization" class="ext"&gt;&lt;cite&gt;Model Context Protocol, Authorization, revision 2026-07-28&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-mcp-resource-parameter" title="#f-agent-mcp-resource-parameter"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="MCP clients must send the RFC 8707 resource parameter with the MCP server&amp;rsquo;s canonical URI in both authorization and token requests, whether or not the authorization server supports it." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/#f-agent-mcp-resource-parameter" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/f-agent-mcp-resource-parameter.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-mcp-audience-validation"&gt;
&lt;blockquote&gt;&lt;p&gt;MCP servers must validate that an access token was issued specifically for them as the intended audience and must not accept or transit any other tokens.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization" class="ext"&gt;&lt;cite&gt;Model Context Protocol, Authorization, revision 2026-07-28&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-mcp-audience-validation" title="#f-agent-mcp-audience-validation"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="MCP servers must validate that an access token was issued specifically for them as the intended audience and must not accept or transit any other tokens." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/#f-agent-mcp-audience-validation" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/f-agent-mcp-audience-validation.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="finding" id="f-agent-mcp-no-token-passthrough"&gt;
&lt;blockquote&gt;&lt;p&gt;An MCP server that calls upstream APIs must use a separate token from the upstream authorization server and must not pass through the token it received from the MCP client.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figcaption&gt;&lt;span class="finding-source"&gt;Source: &lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization/security-considerations" class="ext"&gt;&lt;cite&gt;Model Context Protocol, Authorization Security Considerations, revision 2026-07-28&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;p class="finding-tools"&gt;&lt;a class="finding-id" href="#f-agent-mcp-no-token-passthrough" title="#f-agent-mcp-no-token-passthrough"&gt;Link&lt;span class="sr-only"&gt; to this finding&lt;/span&gt;&lt;/a&gt;&lt;button type="button" class="text-button" data-quote="An MCP server that calls upstream APIs must use a separate token from the upstream authorization server and must not pass through the token it received from the MCP client." data-quote-url="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/#f-agent-mcp-no-token-passthrough" hidden&gt;Copy quote&lt;/button&gt;&lt;a class="finding-card" href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/mcp-authorization/f-agent-mcp-no-token-passthrough.png" download&gt;Save as image&lt;span class="sr-only"&gt; (PNG, 1200 by 630 pixels)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="evidence"&gt;Evidence &lt;a class="heading-anchor" href="#evidence" aria-label="Link to section: Evidence"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="table-scroll studies" tabindex="0" role="region" aria-label="Primary sources on MCP authorization"&gt;&lt;table&gt;
&lt;caption&gt;Primary sources on MCP authorization&lt;/caption&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th scope="col"&gt;Study&lt;/th&gt;&lt;th scope="col"&gt;Year&lt;/th&gt;&lt;th scope="col"&gt;Sample&lt;/th&gt;&lt;th scope="col"&gt;Method&lt;/th&gt;&lt;th scope="col"&gt;Finding&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization" class="ext"&gt;Model Context Protocol: Authorization (revision 2026-07-28)&lt;/a&gt;&lt;span class="org"&gt;Model Context Protocol&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2026&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Defines roles, discovery, the authorization flow, the resource parameter, token handling and step-up authorization for HTTP transports.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization/security-considerations" class="ext"&gt;Model Context Protocol: Authorization Security Considerations (revision 2026-07-28)&lt;/a&gt;&lt;span class="org"&gt;Model Context Protocol&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2026&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Requires audience binding, PKCE, exact redirect URI matching and a ban on token passthrough.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9728" class="ext"&gt;OAuth 2.0 Protected Resource Metadata (RFC 9728)&lt;/a&gt;&lt;span class="org"&gt;IETF&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2025&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Defines the metadata document and the WWW-Authenticate parameter that point a client to a resource&amp;rsquo;s authorization servers.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc8707" class="ext"&gt;Resource Indicators for OAuth 2.0 (RFC 8707)&lt;/a&gt;&lt;span class="org"&gt;IETF&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2020&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Specification&lt;/td&gt;&lt;td&gt;Defines the resource parameter and asks authorization servers to audience-restrict tokens to it.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th scope="row"&gt;&lt;a href="https://www.janbrennenstuhl.eu/talks/economies-of-scale-for-mcp/" class="ext"&gt;Economies of Scale for MCP and Agents&lt;/a&gt;&lt;span class="org"&gt;this site&lt;/span&gt;&lt;/th&gt;&lt;td&gt;2026&lt;/td&gt;&lt;td&gt;n/a&lt;/td&gt;&lt;td&gt;Talk&lt;/td&gt;&lt;td&gt;States that an agent should not hold a shared, long-lived provider credential and presents the broker&amp;rsquo;s delegation model.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id="what-to-do"&gt;What to do &lt;a class="heading-anchor" href="#what-to-do" aria-label="Link to section: What to do"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="for-engineers"&gt;For engineers &lt;a class="heading-anchor" href="#for-engineers" aria-label="Link to section: For engineers"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For an MCP server over HTTP, publish Protected Resource Metadata, answer unauthenticated requests with a 401 that points to it, and check the audience of every token. Put the scopes an operation needs in the &lt;code&gt;WWW-Authenticate&lt;/code&gt; challenge, all at once. If the server calls other APIs, get its own token for each of them. The &lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/"&gt;token exchange entry&lt;/a&gt; shows one way to do that.&lt;/p&gt;
&lt;p&gt;For an MCP client, implement both discovery mechanisms, PKCE with &lt;code&gt;S256&lt;/code&gt;, the &lt;code&gt;resource&lt;/code&gt; parameter, and the issuer check. Never put tokens in the query string.&lt;/p&gt;
&lt;p&gt;An MCP server on the STDIO transport is outside this specification, which says such servers should take credentials from the environment. Keep those credentials short-lived and task-scoped, as my &lt;a href="https://www.janbrennenstuhl.eu/appsec-agentic-engineering/"&gt;AppSec essay&lt;/a&gt; recommends for coding agents.&lt;/p&gt;
&lt;h3 id="for-product-and-ux"&gt;For product and UX &lt;a class="heading-anchor" href="#for-product-and-ux" aria-label="Link to section: For product and UX"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Start with the minimal scopes and ask for more only when an operation needs them. The specification warns that misconfigured scopes may require frequent user interaction, and that challenging one scope at a time forces several authorization round-trips for one operation.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How it works &lt;a class="heading-anchor" href="#how-it-works" aria-label="Link to section: How it works"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The specification defines 3 roles. The MCP server is an OAuth 2.1 resource server, the MCP client is an OAuth 2.1 client acting for a resource owner, and the authorization server issues the tokens. Authorization is optional for MCP implementations. Implementations on HTTP-based transports should conform to the specification, and STDIO implementations should not.&lt;/p&gt;
&lt;p&gt;A first connection follows these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The client sends an MCP request without a token. The server answers with HTTP 401 and a &lt;code&gt;WWW-Authenticate&lt;/code&gt; header that carries the &lt;code&gt;resource_metadata&lt;/code&gt; URL from RFC 9728 and, as the specification recommends, the required &lt;code&gt;scope&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The client fetches the Protected Resource Metadata and picks an authorization server from it. It then reads that server&amp;rsquo;s metadata through OAuth 2.0 Authorization Server Metadata (RFC 8414) or OpenID Connect Discovery. Clients must support both, and if the metadata doesn&amp;rsquo;t list &lt;code&gt;code_challenge_methods_supported&lt;/code&gt;, the client must refuse to proceed.&lt;/li&gt;
&lt;li&gt;The client gets a client ID. It uses pre-registered client information if it has some, then Client ID Metadata Documents if the authorization server supports them. Dynamic Client Registration is deprecated and kept only as a fallback for backwards compatibility.&lt;/li&gt;
&lt;li&gt;The client starts the authorization code flow with PKCE, the &lt;code&gt;resource&lt;/code&gt; parameter and the selected scopes, and records the expected issuer. When the response comes back, it validates the &lt;code&gt;iss&lt;/code&gt; parameter (RFC 9207) before it redeems the code.&lt;/li&gt;
&lt;li&gt;The client redeems the code with the &lt;code&gt;code_verifier&lt;/code&gt; and the &lt;code&gt;resource&lt;/code&gt; parameter, then sends the access token in the &lt;code&gt;Authorization: Bearer&lt;/code&gt; header on every request.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When a token lacks a scope at runtime, the server answers with 403 and &lt;code&gt;error=&amp;quot;insufficient_scope&amp;quot;&lt;/code&gt;. The client then runs a step-up flow with the union of its previous scopes and the new ones.&lt;/p&gt;
&lt;p&gt;Revision 2026-07-28 doesn&amp;rsquo;t cover token exchange in its core authorization pages. Optional authorization extensions live in a separate repository. In the Agentic Identity Broker, which Magnus Jungsbluth and I built, an MCP gateway exchanges the agent&amp;rsquo;s token for the matching provider token through RFC 8693, so the provider token never reaches the agent. I describe that design in the &lt;a href="https://www.janbrennenstuhl.eu/agentic-identity-broker/"&gt;retrospective&lt;/a&gt;, and we presented it in our &lt;a href="https://www.janbrennenstuhl.eu/talks/economies-of-scale-for-mcp/"&gt;AGNTCon + MCPCon talk&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="related"&gt;Related &lt;a class="heading-anchor" href="#related" aria-label="Link to section: Related"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/delegation-vs-robot-accounts/"&gt;Should an agent use a robot account or delegated access?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/consent-and-revocation/"&gt;How should people grant and revoke an agent&amp;rsquo;s access?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.janbrennenstuhl.eu/projects/agentic-identity-broker/"&gt;Agentic Identity Broker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>