Agents Need Identities, Not Shared API Keys: A Least-Privilege Playbook for 2026

Survey data shows agent adoption outrunning security. How Entra Agent IDs, MCP auth changes, and a deny-by-default tool gate (tested C#) give AI agents least privilege.

By Ajith joseph · Tue Sep 29 2026 · Updated Tue Sep 29 2026 · 9 min read · intermediate

#dotnet #security #ai #agents #copilot-studio

There is a familiar shape to this story. A new kind of software gets adopted faster than the controls around it, everyone is confident it is fine, and then someone reads the logs.

AI agents are at that stage. A survey published in February 2026 found most teams past the planning phase with agents and only a small share with full security approval. The gap between "running" and "approved" is the risk, and the specific place it shows up is identity: what an agent is, what it may touch, and how anyone would know.

This post lays out the numbers with the caveats they deserve, describes what Microsoft now provides for agent identity in Copilot Studio, and builds the piece you always end up needing regardless of platform: a deny-by-default gate in front of an agent's tool calls, with an audit trail. The code is tested.

What the Survey Found, and How Much to Trust It

The report is Gravitee's State of AI Agent Security 2026, published 4 February 2026, based on a survey of more than 900 executives and technical practitioners. Gravitee sells API management, so it has an interest in this topic, and the figures are self-reported. Read them as directional, and note they are from February, not September.

The headline numbers:

  • 81% of teams are past the planning phase (80.9% of technical teams are in active testing or production), yet only 14.4% have full security approval
  • 88% of organisations confirmed or suspected a security incident during the survey year
  • Only 21.9% treat agents as independent, identity-bearing entities
  • 45.6% still rely on shared API keys for agent-to-agent authentication, and 27.2% use custom hardcoded authorization logic
  • On average 47.1% of an organisation's agents are actively monitored or secured
  • 25.5% of deployed agents can create and task other agents
  • 82% of executives feel confident their existing policies protect against unauthorised agent actions

That last figure next to the others is the story. Confidence is high, and the controls that would justify it are mostly absent. Shared API keys are the clearest example: a shared key cannot tell you which agent did something, cannot be revoked for one agent without breaking others, and grants the same power to all of them.

What Copilot Studio Gives You

Microsoft has made agent identity a platform feature rather than a convention. From Microsoft Learn's page on managing Entra Agent IDs:

  • Copilot Studio automatically creates a Microsoft Entra Agent ID for each new agent, and you can no longer opt out at the environment level.
  • Existing agents that predate the rollout continue to use app registrations, and Microsoft will migrate them in a future update. You can migrate older agents yourself through the Power Platform admin center, PowerShell scripts or the Power Platform APIs. Governance works for both during the transition.
  • The identity gives you audit logging in Microsoft Entra ID, agent lifecycle management, and integration with Entra ID Governance.
  • When a maker publishes an agent, Copilot Studio attaches API permissions to the agent's identity for each Power Platform connector it is configured to use. Admins can see what an agent can call from the Entra admin center without opening the Power Platform admin center.
  • Those connector permissions can be targeted with Conditional Access policies, for example requiring a network location, device compliance or risk level before tokens are issued for a specific connector.
  • The connector runtime is the only component that honours those scopes, and it revalidates them against connector and data policies, so they cannot be used to bypass governance.
  • Deleting an agent deletes its identity.

A note on dates: Microsoft's pages disagree slightly on when this rolled out. The identity page says May 2026, while the What's new page lists the no-opt-out change under July 2026. The behaviour is the same in both, that new agents get an Entra Agent ID automatically, so plan around the behaviour and not the month.

To confirm an agent has one, open its Settings, then Advanced, and expand Metadata. The GUID appears under Entra Agent ID.

Two related admin features from the What's new page are worth turning on. The agent inventory schema lets you discover and audit every Copilot Studio agent in the organisation from the admin center, an API or Azure Resource Graph, which is how you find the agents nobody told you about. And environment-level agent telemetry can be exported to Application Insights, though that one was in preview as of July.

What the MCP Spec Adds

If your agents call tools over MCP, the July 2026 specification tightened authorization in ways that support the same goal:

  • Clients must validate the iss parameter in authorization responses before redeeming a code (RFC 9207)
  • Client credentials are bound to the authorization server that issued them, and clients must not reuse them with a different one
  • Dynamic Client Registration is deprecated in favour of Client ID Metadata Documents

None of that gives you per-tool policy, which is why the next section exists. Identity answers who the agent is. It does not decide what that agent may do with each tool on a given call.

The Gate

Whatever platform you use, the enforcement point belongs in the tool layer, in code you control, not in the prompt. A prompt that says "never delete customers" is a request. A gate that has no delete tool in the agent's allowlist is a fact.

The gate below is small on purpose. It has one policy per agent identity, denies by default, requires a human decision for write tools, rate limits per agent, and writes every decision to an audit sink, including the denials, because the denials are the interesting part of an incident review.

using System.Collections.Concurrent;

namespace AgentGate;

public enum Verdict { Allow, Deny, NeedsApproval, RateLimited }

public sealed record ToolPolicy(
    IReadOnlySet<string> Allowed,
    IReadOnlySet<string> RequireApproval,
    int MaxCallsPerMinute);

public sealed record Decision(Verdict Verdict, string Reason)
{
    public bool IsAllowed => Verdict == Verdict.Allow;
}

public sealed class AgentToolGate(
    IReadOnlyDictionary<string, ToolPolicy> policies,
    TimeProvider clock,
    Action<string> audit)
{
    private readonly ConcurrentDictionary<string, Queue<DateTimeOffset>> _calls = new();

    public Decision Check(string agentId, string tool, bool humanApproved = false)
    {
        var decision = Evaluate(agentId, tool, humanApproved);
        audit(
quot;{clock.GetUtcNow():O} agent={agentId} tool={tool} verdict={decision.Verdict} reason={decision.Reason}"); return decision; } private Decision Evaluate(string agentId, string tool, bool humanApproved) { if (!policies.TryGetValue(agentId, out var policy)) return new(Verdict.Deny, "unknown agent identity"); if (!policy.Allowed.Contains(tool)) return new(Verdict.Deny, "tool not in this agent's allowlist"); if (policy.RequireApproval.Contains(tool) && !humanApproved) return new(Verdict.NeedsApproval, "write tool needs a human decision"); var window = _calls.GetOrAdd(agentId, _ => new Queue<DateTimeOffset>()); var now = clock.GetUtcNow(); lock (window) { while (window.Count > 0 && now - window.Peek() >= TimeSpan.FromMinutes(1)) window.Dequeue(); if (window.Count >= policy.MaxCallsPerMinute) return new(Verdict.RateLimited,
quot;more than {policy.MaxCallsPerMinute} calls in a minute"); window.Enqueue(now); } return new(Verdict.Allow, "ok"); } }

Two design choices to call out. The clock is injected as a TimeProvider, so the rate limit is testable without sleeping. And the agent identifier is whatever your platform gives you, which in Copilot Studio should be the Entra Agent ID rather than a display name, because a name can be edited and an identity cannot be reused by accident.

The self-check drives every branch with a controllable clock, and it passes:

ok   unknown agent is denied
ok   tool outside allowlist is denied
ok   write tool waits for a human
ok   write tool runs once approved
ok   call 2 in the window
ok   call 3 in the window
ok   call 4 in a minute is rate limited
ok   window slides after a minute
ok   every decision is audited, denied ones included
agent gate: all checks passed

Three things this sketch does not do, because you should decide them deliberately. It keeps its rate-limit state in memory, so behind a load balancer you need a shared store. The humanApproved flag is only as trustworthy as whatever sets it, so the approval must come from a channel the agent cannot write to. And the policy table is in code here, where in practice it should live in configuration your security team can review.

Putting It Together

The pattern has five links, and a weakness in any one undermines the rest:

  1. An identity per agent, so actions are attributable. Use the platform's identity, not a shared key
  2. A policy per identity, listing exactly which tools it may call
  3. A gate in the tool layer, denying by default and enforcing that policy in code
  4. A human decision for anything irreversible, coming from a channel the agent cannot influence
  5. An audit trail of every decision, denials included, sent somewhere the agent cannot edit

The 25.5% figure is worth a specific decision. If an agent can create and task other agents, each new agent needs its own identity and policy, and the parent must not be able to grant a child more than it holds. If you cannot enforce that, do not let agents spawn agents.

Checklist

  • Inventory every agent, using the agent inventory schema on Copilot Studio, and find the ones with no owner
  • Confirm each new agent has an Entra Agent ID, and migrate older agents that still use app registrations
  • Remove shared API keys between agents and replace them with per-agent credentials
  • Review the connector permissions on each agent identity in the Entra admin center, and apply Conditional Access where the data is sensitive
  • Put a deny-by-default gate in front of tools, with an allowlist per agent
  • Require human approval for write tools, delivered through a channel the agent cannot reach
  • Send tool-call audit logs and agent telemetry somewhere they cannot be altered, and alert on denials
  • Decide explicitly whether agents may create other agents, and cap what a child can inherit
  • Treat any executive confidence about agent controls as a hypothesis until the audit log confirms it

Agents are becoming ordinary participants in your systems, and ordinary participants have identities, permissions and logs. The platforms are finally providing the first of those. The other two are still yours to build.

Sources

  • State of AI Agent Security 2026 Report: When adoption outpaces control, Gravitee, 4 February 2026 (vendor-run survey of 900+ respondents)
  • Manage Entra Agent IDs, Microsoft Learn
  • What's new in Copilot Studio, Microsoft Learn
  • Key changes, MCP specification 2026-07-28
  1. AJ's Tech Notes
  2. Agents Need Identities, Not Shared API Keys: A Least-Privilege Playbook for 2026