Developers are building AI agents at a breakneck pace to automate everything from customer support to infrastructure management. These AI agents are very robust and can read emails, query production databases, and even deploy servers. While the capabilities of these AI machines have evolved, the way we secure them has not.
Most of the AI agents today are operated using static API keys with broad, long-lived permissions. That means that if an attacker tweaks the agent via prompt injection, they could gain possession of admin keys. This security model is outdated and breaks the very core principles of Zero Trust.
First, we’ll explore why AI agents need distinct identities, and then we’ll walk through how to implement the Model Context Protocol (MCP) to give AI agents least-privilege access and ensure they act only when authorized.
The “superuser” problem
We have spent the last decade moving away from shared passwords for humans, yet we often treat AI agents like superusers, granting them hardcoded secrets. When we insert a static API token directly into an agent’s environment variables, we create a big attack surface. This token lacks context–the system knows that the API token was used, but not why or by whom the agent was triggered.
Agents can hallucinate or be manipulated; once they do, they can perform actions that are difficult to trace, and it can be even harder to revoke access without breaking the entire application. To fix this issue, we need to treat AI agents with the same identity-first mindset we apply to human employees.
The “rotation” nightmare
Let’s be honest, nobody rotates static keys as often as they should because it’s a logistical nightmare.
If we have a single OKTA_API_TOKEN hardcoded into three different microservices, a CI/CD pipeline, and a local .env file for testing, rotating that key isn’t a security task – it’s an outage waiting to happen. To do this right, we have to generate a new key, find every repo using the old key, redeploy all services, and pray we didn’t miss one small cron job running on some legacy server.
Because this process is so painful, teams delay it. I’ve seen some production keys that haven’t been rotated in years because the original developer who created them left, and no one wants to be the one to break the build by changing them. This creates a massive window of vulnerability: if that key leaks, the attacker has permanent access until you panic-rotate it.
The solution: The “on-behalf-of” flow
To secure this new wave of automation, we need to shift from static keys to dynamic token exchange (defined in RFC 8693). In identity architecture, we know this as the on-behalf-of (OBO) flow. Here, the agent acts as a delegate rather than a God Mode administrator. When a user asks the agent to do something, the agent authenticates itself but requests permission to act on behalf of that specific user.
This requires two key components:
- A standard protocol: We need a way for the AI to standardize its requests; the Model Context Protocol (MCP) is where this comes into play.
- Identity assertion: We need a way to swap the user’s session for a temporary, scoped access token for the agent.
Implementing the Model Context Protocol (MCP)
Now, we’ll look at how we can implement this pattern practically. For this, we’ll use the open-source Okta MCP Server to handle the connection between our LLM (like Github Copilot or Claude) and your identity provider (IdP).
Step 1: Remove Static secrets
First, we’ll audit our agent’s code. For this, identify any environment variables that store long-lived secrets, for example, OKTA_API_TOKEN, and delete all of them. Using static keys for dynamic agents is the security equivalent of sharing your banking password with a temporary contractor. The goal is to ensure the agent has zero privileges when it’s idle.
Step 2: Configure scoped permissions
In your respective IdP, register this agent as a distinct application. This allows us to define exactly what it can and can’t do. Instead of granting read/write access, try using granular scopes. For example, it shouldn’t have explicit permissions like users.delete. In Okta, this is handled via Cross-App Access (XAA), which allows us to bind these specific permissions to the agent’s unique client ID.
Step 3: The token exchange
Now, we’ll look at the technical flow when a user prompts the agent (e.g., “Reset my password”):
- The trigger: User sends a prompt to the agent via their IDE or chat interface.
- The handshake: The agent then connects to the MCP server using the standard protocol.
- The exchange: The server intercepts the request and performs an identity assertion. It says to the IdP “I am a password-reset agent. User-X has just authorized me. Please give me a token valid for five minutes.”
- The action: The IdP then issues a short-lived token valid only for that specific task of password reset.
The result: Audit-ready agents
When we implement this architecture, we transform our vague logs into a clear stacktrace.
Instead of displaying a generic system action, our logs will show:
“Password-Reset Agent accessed the Password API on behalf of User-X.”
This granular visibility allows security teams to run access reviews on AI agents, just like they do for regular employees. If, for some reason, an AI agent starts acting suspiciously, we can revoke its specific access policy instantly without rotating keys for the entire engineering team.
From static keys to secure autonomy
As AI agents evolve and become more autonomous, we can’t afford to secure them with the same old static keys we used for cron jobs in 2015.
Once we adopt the Model Context Protocol and Identity Assertion (XAA), we can have AI agents that are helpful, capable, and most importantly, secure.
Ready to start building? Check out the open-source Okta MCP Server. This reference implementation provides a bridge between your AI agent and any IdP to enable a secure on-behalf-of token exchange.