Skip to content

Stop giving AI agents service accounts

Published:
• 9 min read
Stop giving AI agents service accounts

The easiest way to get an AI agent into production is also one of the easiest ways to lose track of what it is allowed to do.

Give it a service account.

Give that account access to the APIs it needs.

Put the credential in a vault.

Done.

We have used that pattern for years. It works well for software that has a stable job: a batch process reads from one database, an integration moves orders from A to B, a monitoring job opens incidents.

An agent is not quite that.

An agent might read a ticket, inspect a customer, query an MCP server, call three APIs, decide that it needs another tool, and then update something — all because a person asked a fairly innocent question in a chat window.

That is a very different security shape.

And I think we are going to regret treating those agents as ordinary service accounts.

A service account tells me what the application can do

Imagine a support agent.

I ask:

Why is this customer’s order stuck?

The agent checks the order, inventory, provisioning state and recent incidents. So far, read-only.

Then I ask:

Can you fix it?

Now the same agent may need to retry an activation, update a ticket or trigger a workflow.

If every call arrives downstream as svc-ai-agent-prod, we have thrown away some of the most useful context before the request even reaches the business system.

Who asked?

Which agent is acting?

Is it acting for that person, or on its own?

What task is it performing?

Was this specific action approved?

Which permissions should apply right now?

A service account can answer one of those questions reasonably well: which technical principal made the call.

That is no longer enough.

This is becoming an identity problem, not an AI problem

The interesting signal is that identity vendors and cloud platforms are converging on roughly the same problem.

NIST recently called out long-lived API keys and bearer tokens as a weak foundation for agentic systems. Their argument is straightforward: possession of a static key does not prove who is using it, the credentials tend to be broad, and agents move them across more tools and resources than traditional applications do.

Microsoft Foundry now explicitly separates agent identity from user identity. Its model supports both attended agents acting on behalf of a person and unattended agents operating under their own authority. For attended use, the token can carry the agent identity together with the user’s delegated permissions.

AWS has moved its MCP authentication in the same direction. Interactive MCP access can use OAuth authorization code flow, while automated agents can obtain short-lived OAuth access tokens through client credentials instead of carrying a permanent MCP key.

Okta and Auth0 are using the term I like most: agent as principal.

Not the user.

Not a generic application account.

The agent itself becomes an identity that can be permissioned, governed and audited.

That feels like the right mental model.

The identity chain matters

For an attended enterprise agent, I want the request chain to look something like this:

%%{init: {"look": "handDrawn", "theme": "base", "themeVariables": {"fontFamily": "Comic Sans MS, Virgil, Arial", "primaryColor": "#fff8dc", "primaryBorderColor": "#333", "lineColor": "#333"}}}%%
flowchart LR
  user["User<br/>Who asked?"]
  agent["Agent<br/>What is acting?"]
  policy["Policy / Authorization<br/>Is this action allowed?"]
  tool["MCP / API / Business System<br/>Scoped access"]

  user -->|authenticated user| agent
  agent -->|agent identity + delegated user context| policy
  policy -->|short-lived scoped credential| tool

There are two identities in that picture for a reason.

The user tells us who asked.

The agent tells us what is acting.

The authorization layer decides whether this combination is allowed to perform this action in this context.

That is much more useful than:

%%{init: {"look": "handDrawn", "theme": "base", "themeVariables": {"fontFamily": "Comic Sans MS, Virgil, Arial", "primaryColor": "#fff8dc", "primaryBorderColor": "#333", "lineColor": "#333"}}}%%
flowchart LR
  agent["AI Agent"]
  service["svc-ai-prod"]
  everything["Everything"]

  agent --> service --> everything

The second version is easy to build.

It is also how you end up staring at an audit log six months later where every action was performed by the same account.

On-behalf-of should not mean “become the user”

There is a trap on the other side too.

If shared service accounts are too broad, it is tempting to simply pass the user’s identity through and let the agent become the user.

I do not like that either.

If I can change 200 customer records manually, it does not follow that every agent I use should automatically inherit the ability to change 200 customer records.

The useful permission is the intersection:

what the user may do
∩ what this agent may do
∩ what this task requires

If I use a read-only assurance agent, my own write permissions should not magically turn it into a provisioning agent.

Likewise, a provisioning agent with powerful capabilities should not be able to exercise them for a user who is only allowed to inspect service state.

That intersection is where agent authorization starts to make sense.

Short-lived credentials change the failure mode

There is another reason I want this outside the model: credentials.

An agent should not know a password.

It should not have a permanent API token sitting in its configuration.

And the LLM definitely should not see the credential.

The runtime should obtain a short-lived token for the specific resource, with the smallest useful scope, and hand it to the deterministic tool layer.

That is not a theoretical design anymore. AWS’s MCP server documentation describes one-hour OAuth access tokens. Microsoft recommends Entra-based authentication when possible specifically to avoid managing shared secrets, and supports OAuth identity passthrough when the user’s context needs to survive the trip to an MCP server.

The important part is not whether the token lives for exactly one hour.

The important part is that access is issued, scoped, expires and can be revoked.

A leaked permanent service-account credential is an incident.

A short-lived token for one agent, one resource and a limited set of permissions is a much smaller problem.

MCP makes this more obvious

MCP is making tool access wonderfully easy.

That is also why identity becomes harder to ignore.

An agent may have access to an Order MCP, Inventory MCP, ServiceNow MCP and some internal platform tools. Each server may expose several capabilities. Some are reads. Some change state.

I do not want every MCP server inventing its own answer to identity.

And I do not want the LLM deciding whether the current user is authorized.

The model can decide:

I need the retry_activation tool.

Fine.

The platform should decide:

This agent is registered for that capability. Nikolaj is allowed to request it for this customer. The action requires write access. Policy says this operation needs approval. Issue a scoped credential after approval.

That split is important.

The LLM chooses what might help.

The platform decides what is allowed.

Unattended agents are the harder case

Not every agent has a person sitting in front of it.

A scheduled incident agent might inspect failures every five minutes and create a ServiceNow ticket when something looks wrong. A housekeeping agent might clean stale resources overnight. A deployment agent might react to a pipeline event.

There is no user to delegate from.

That does not mean we go back to svc-ai-prod.

The agent should still have its own identity.

It should have an owner.

It should have a defined purpose.

It should have explicit resource connections and scopes.

And it should be possible to disable that agent without disabling ten unrelated automations that happened to share the same account.

This is where agent registries start to become more than catalogs.

Identity, ownership and lifecycle belong together.

If an agent is retired, its access should disappear with it.

What I would actually build

I would keep the first version boring.

Every production agent gets a unique identity and an owner.

Every run keeps both the initiating user identity, when there is one, and the agent identity.

Access to downstream tools is issued at runtime using short-lived credentials.

Authorization checks the user, agent, requested action and resource. Not just the token at the front door.

MCP servers and APIs receive enough trusted context to make a real authorization decision.

And the audit trail records the chain:

%%{init: {"look": "handDrawn", "theme": "base", "themeVariables": {"fontFamily": "Comic Sans MS, Virgil, Arial", "primaryColor": "#fff8dc", "primaryBorderColor": "#333", "lineColor": "#333"}}}%%
flowchart LR
  user["User"]
  agent["Agent"]
  policy["Policy decision"]
  tool["Tool"]
  object["Business object"]
  result["Result"]

  user --> agent --> policy --> tool --> object --> result

For higher-risk writes, add approval before the credential or tool invocation is released.

None of that requires the LLM to become a security component.

That is the nice part.

Service accounts are not going away

There will still be service accounts.

Some old systems only support username/password credentials. Some APIs only understand API keys. Some integrations genuinely are fixed machine-to-machine processes.

The answer is not to pretend those systems disappear.

Put the ugly credential behind the platform.

Vault it.

Exchange into it as late as possible.

Limit which agent can request it.

Keep the user and agent context in the audit trail even if the final legacy hop unfortunately says svc_something.

Okta’s own agent token-exchange documentation is interesting here because it explicitly supports brokering access to existing service accounts and secrets. That is much more realistic than assuming every enterprise application will suddenly implement modern agent identity.

Modern identity at the control plane. Compatibility at the edge.

I can live with that.

The question I would ask before connecting an agent

When somebody wants to give an agent access to another system, I would stop asking only:

Which service account should we create?

I would ask:

Who is the agent when it makes this call?

Then:

Who is it acting for?

And finally:

Why is it allowed to do this?

If the answer to all three is the same shared username and a secret in a vault, the agent architecture is probably not finished.

The model is the new part.

Identity is not.

We already know how to build least privilege, delegation, short-lived credentials, policy enforcement and audit trails.

Agents are simply forcing us to use those ideas properly.

Sources



Related reading

More pieces in the same part of the map.