<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>OAuth | The .NET Blog</title><link>https://thedotnetblog.com/tags/oauth/</link><description>Articles, tutorials and insights from the .NET community.</description><generator>Hugo</generator><language>en</language><managingEditor>@thedotnetblog (The .NET Blog)</managingEditor><webMaster>@thedotnetblog</webMaster><lastBuildDate>Wed, 20 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://thedotnetblog.com/tags/oauth/index.xml" rel="self" type="application/rss+xml"/><item><title>Your AI Agent Has an Identity Problem (And Here's the Template That Solves It)</title><link>https://thedotnetblog.com/news/emiliano-montesdeoca/azd-least-privilege-ai-agents-oauth-token-pattern/</link><pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/news/emiliano-montesdeoca/azd-least-privilege-ai-agents-oauth-token-pattern/</guid><description>A new azd template from Curity and Microsoft shows how to build AI agents that use short-lived OAuth tokens with fine-grained scopes — so agents can never see data they shouldn't.</description><content:encoded>&lt;p&gt;There&amp;rsquo;s a moment in every AI agent project that goes something like this: the demo works perfectly, the agent interprets natural language, calls the right APIs, returns the right data. Then you start thinking about real users.&lt;/p&gt;
&lt;p&gt;What stops one user&amp;rsquo;s agent session from seeing another user&amp;rsquo;s data? What if the agent is tricked through prompt injection? What if it calls a tool in an unexpected way?&lt;/p&gt;
&lt;p&gt;These aren&amp;rsquo;t edge cases. They&amp;rsquo;re design decisions you need to make before shipping.&lt;/p&gt;
&lt;p&gt;A new &lt;code&gt;azd&lt;/code&gt; template from Curity and Microsoft gives you a working reference for exactly this problem.&lt;/p&gt;
&lt;h2 id="the-core-problem-authentication--authorization"&gt;The Core Problem: Authentication ≠ Authorization&lt;/h2&gt;
&lt;p&gt;Most agent samples handle user authentication well. They handle authorization poorly. Knowing &lt;em&gt;who&lt;/em&gt; the user is doesn&amp;rsquo;t tell you &lt;em&gt;what data&lt;/em&gt; they should see.&lt;/p&gt;
&lt;p&gt;A traditional client app makes predictable API calls. An AI agent is nondeterministic — it interprets natural language and decides what to call. It can be creative. It can also be wrong. And if it&amp;rsquo;s manipulated through prompt injection, you need rules that don&amp;rsquo;t depend on the AI being well-behaved.&lt;/p&gt;
&lt;p&gt;The solution this template demonstrates: &lt;strong&gt;short-lived tokens that carry exactly the right information for each hop&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="how-the-token-chain-works"&gt;How the Token Chain Works&lt;/h2&gt;
&lt;p&gt;The template uses OAuth 2.0 access tokens with token exchange to narrow permissions at each step. A user token gets exchanged twice before it reaches the MCP server:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;First exchange&lt;/strong&gt; — narrows the scope and converts the opaque token to a JWT&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Second exchange&lt;/strong&gt; — adds the agent identity and a new audience for the MCP server hop&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;What the MCP server token looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;scope&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;stocks/read&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sub&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;62c839b8...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;aud&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://mcp.demo.example&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;customer_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;178&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;region&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;USA&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;customer_id&lt;/code&gt; is baked into the token by the authorization server, not passed as a parameter the agent controls. The API checks the token, not the agent&amp;rsquo;s instructions.&lt;/p&gt;
&lt;p&gt;This means: even if someone tricks the agent into trying to fetch another customer&amp;rsquo;s data, the token won&amp;rsquo;t authorize it.&lt;/p&gt;
&lt;h2 id="what-the-template-deploys"&gt;What the Template Deploys&lt;/h2&gt;
&lt;p&gt;With a few &lt;code&gt;azd&lt;/code&gt; commands you get:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A backend agent on Microsoft Foundry (C#, Microsoft A2A and MCP SDKs)&lt;/li&gt;
&lt;li&gt;An MCP server exposing a sample portfolio API&lt;/li&gt;
&lt;li&gt;Curity Identity Server as the authorization server, alongside Entra ID for authentication&lt;/li&gt;
&lt;li&gt;External and internal API gateways handling token exchange and audit logging&lt;/li&gt;
&lt;li&gt;Bicep for all the Azure infrastructure: Container Apps, VNet, ACR, Azure AI Foundry, Key Vault, Azure SQL Database, storage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The whole pattern is inspectable and customizable.&lt;/p&gt;
&lt;h2 id="the-design-principle-worth-borrowing"&gt;The Design Principle Worth Borrowing&lt;/h2&gt;
&lt;p&gt;Even if you don&amp;rsquo;t use Curity, the pattern is transferable: &lt;strong&gt;agents should never hold permanent API access&lt;/strong&gt;. Every action should use a short-lived token with the minimum scope needed for that specific call, issued to the specific agent identity, carrying the claims the API needs to make authorization decisions.&lt;/p&gt;
&lt;p&gt;This holds up against creative agents, mistakes, and prompt injection in ways that &amp;ldquo;just make sure the agent doesn&amp;rsquo;t do bad things&amp;rdquo; never will.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Security patterns for AI agents are still being worked out across the industry. This template is one of the more complete reference implementations I&amp;rsquo;ve seen — it covers the actual authorization flow, not just authentication.&lt;/p&gt;
&lt;p&gt;Original post: &lt;a href="https://devblogs.microsoft.com/azure-sdk/azd-curity-least-privilege-ai-agents/"&gt;Least privilege AI agents: A new azd template from Curity and Microsoft&lt;/a&gt;&lt;/p&gt;</content:encoded></item></channel></rss>