Classic painting used as the article cover
← Back to blog

AGENT SECURITY

Agent Credentials vs. Human Credentials

A human credential proves possession of two factors. An agent credential proves nothing about anyone. What RFC 8693's act claim, SPIFFE SVIDs and non-expiring service-account keys say about the difference.

•Jul 29, 2026•Updated Oct 13, 2026•15 min
CredentialsIdentitySecurity

TL;DR

An agent credential and a human credential are different objects, and the standards say so in their own definitions. Treating them as one is why agent audit trails collapse under the question "who actually did this?"

NIST SP 800-63B defines human authentication as verifying that a claimant "controls one or more authenticators" bound to a subscriber account, and requires "two distinct authentication factors" at AAL2. None of that applies to an agent. There is nobody to challenge, nothing to possess.

The instinct is to treat this as a logging problem that more monitoring would fix. It is an identity problem. RFC 8693 draws the line precisely: under impersonation the actor "is indistinguishable from B in that context", while under delegation "principal A still has its own identity separate from B". Hand an agent a person's login and you have chosen impersonation, and no amount of instrumentation recovers what the token threw away.

We are not arguing an agent needs human-shaped access. It needs narrower access that is unmistakably its own, and a record that names both parties.

The fix is cheap to start. Stop issuing keys that never expire, give the agent an identifier of its own, and make every call carry two subjects instead of one.

Overview

An AI agent sits awkwardly in the identity model most enterprises already run. It is not a person, so the human toolkit does not apply in a way that still means anything: no one to prompt for a second factor, no device posture to check, no conditional-access rule written with it in mind. It is not an invisible background job either, because it takes consequential actions, sometimes on its own initiative.

Microsoft's own taxonomy makes the split explicit before you get to any product detail. "At a high level, there are two types of identities: human and machine/non-human identities", and machine identities break down further into device and workload identities. An agent is a workload that behaves like a user, which is exactly why the categories fight each other.

Most teams resolve the access question the fast way. Point the agent at a human's login, or drop it onto a service account that already exists. Both get an agent working by lunchtime. Neither survives the first serious question a reviewer asks.

This is the definitional question underneath the rest of the credential-management family. Rotation schedules, vaults and monitoring dashboards all assume the credential they operate on belongs to one agent and nobody else. Skip that and the machinery above it stops proving anything.

We think the shortcut is the mistake, not the agent. Give a token the shape of a person's login and it keeps behaving like one, whatever sits on the other end. That mismatch shows up first in the audit trail, long before it shows up in an incident.

Two Kinds of Credentials

Human credentials are tied to people, and they live inside a stack that matured over decades. NIST SP 800-63B describes the mechanism plainly: authentication verifies that the claimant "controls one or more authenticators" associated with a subscriber account, and at AAL2 "proof of possession and control of two distinct authentication factors through the use of secure authentication protocols is required". Every word there assumes a body. Possession, control, a claimant who can be challenged.

Agent credentials are tied to non-human actors: API keys, OAuth tokens, service accounts, delegated credentials. None of the human assumptions hold. There is no one to prompt, no device to inspect, no person whose behaviour the rule was written around.

The differences that actually bite are in lifetime and attribution, and those are documented per platform. The last two columns are terms in the taxonomy's credential model dimension.

PropertyHuman credentialLong-lived agent key, aact:credential-model/static-secret-in-agentManaged workload identity, aact:credential-model/credential-never-exposed
What is provenPossession and control of authenticators, two distinct factors at AAL2Possession of a stringPossession of a short-lived document issued to this workload
Default lifetimeSession-bound, re-challenged on riskGoogle IAM service-account keys "don't have an expiry time and stay valid until you delete them"AWS temporary credentials run 900 seconds to 36 hours, default 12; SPIFFE SVIDs are "short lived, rotated frequently and automatically"
Who holds the secretThe personWhoever has the file, and every copy of itNobody. Azure managed identities: "Credentials aren't even accessible to you"
RotationPassword or factor change, user-drivenManual, and usually neverAutomatic, a property of the platform
Attribution in the logThe named personThe key's account, whoever used itThe workload identifier

Table 1 — The same row that makes a human credential meaningful is the row an agent key leaves blank.

Google's guidance on its own key format is unusually direct about the consequence: because the private key authenticates as the service account, "having access to the private key is similar to knowing a user's password", and "the best way to mitigate these threats is to avoid user-managed service account keys". SPIFFE takes the other route and removes the secret entirely, noting that with its Workload API "your application need not co-deploy any authentication secrets with the workload".

The mistake is not choosing the wrong format. It is reusing the human toolkit for the non-human case. Controls designed for a person, layered onto a credential standing in for something that is not one, do not fail loudly. They quietly stop proving anything.

The Borrowed-Access Problem

When an agent uses a human's credential, it inherits everything that person can do, and every action is recorded as if the person performed it. The taxonomy calls that aact:identity-model/shared-human-identity.

Take a hypothetical financial close assistant running under a controller's account, because that was the simplest way to reach the general ledger. It can post journal entries, reclassify accounts and pull bank reconciliation data: the full surface of a senior finance role. When the entries are reviewed three weeks later, the ledger shows the controller made them. Nothing in the record separates human judgment from an agent acting on a rule it inferred.

Two problems arrive together. Auditability weakens, because you cannot separate what the human did from what the agent did when both wear one identity. And risk expands, because the agent can perform everything the user can, including actions far outside its narrow task.

MCP's specification treats the wire-level version of this as a hard error rather than a style preference. Servers "MUST NOT accept or transit any other tokens" than ones issued for them, and the security guidance explains why in attribution terms: when tokens are forwarded rather than reissued, "the downstream Resource Server's logs may show requests that appear to come from a different source with a different identity". That is borrowed access described from the provider's side of the call.

Borrowed access feels like delegation. It is impersonation. The system cannot tell the difference, so your controls cannot either.

The Shared-Account Problem

The other shortcut runs the opposite way: many agents, one shared service account. It is appealing because a single set of keys is easy to provision and rotate, and it sidesteps agent identity altogether.

Picture a data platform where six analytics agents, one summarising pipeline health, one refreshing dashboards, one flagging anomalies, one backfilling tables, all authenticate to the warehouse as the same service account. One night a destructive query truncates a production table. The warehouse log says the action came from svc-analytics. Which agent issued it? Nobody can say.

Worth noting that sharing is often a documented feature rather than an accident. Azure's user-assigned managed identities "can be shared", and "the same user-assigned managed identity can be associated with more than one Azure resource", which Microsoft recommends for workloads that genuinely are one thing spread across machines. The trap is using that shape for workloads that are several different actors, because then the identity stops answering the question you will eventually ask it.

An agent needs to reach a system

Borrow a human's login

Share one service account
across several agents

Issue the agent its own
scoped identity

Log shows the human.
The agent is invisible.

Log shows 'svc-account'.
No single agent is named.

Log shows the agent
and the user it acted for.

Who did this?
Unanswerable.

Who did this?
Answerable, and scoped.

log shows the human -----> unanswerable |-- share one service account -> log shows "svc-account" -> unanswerable |-- issue its own identity ----> log shows agent + user --> answerable, scoped -->

Figure 1 — Three ways to give an agent access, and what each leaves behind in the log.

Impersonation vs. Delegation

The distinction the whole post rests on already has a normative definition, and it is worth reading rather than paraphrasing. RFC 8693, OAuth 2.0 Token Exchange, describes impersonation as the case where "principal A is given all the rights that B has within some defined rights context and is indistinguishable from B in that context". Delegation is different: "principal A still has its own identity separate from B, and it is explicitly understood that while B may have delegated some of its rights to A, any actions taken are being taken by A representing B."

Indistinguishable is the operative word. An agent on a human's credential is not merely badly-labelled. It is, by the standard's own definition, the same principal.

The spec also defines the claim that expresses the other choice. The act claim "provides a means within a JWT to express that delegation has occurred and identify the acting party to whom authority has been delegated", and chains nest, where "the outermost act claim represents the current actor while nested act claims represent prior actors". A companion claim, may_act, "makes a statement that one party is authorized to become the actor and act on behalf of another party", which is the part that stops an agent from nominating itself as anyone's delegate.

json
{
  "iss": "https://issuer.example.com",
  "aud": "https://ledger.internal.example.com",
  "sub": "user:controller@enterprise.example",
  "act": { "sub": "agent:close-assistant-v4" },
  "scope": "journal_entries:draft",
  "exp": 1774000000
}
Ledger APIAuthorization serverClose assistant (agent)Controller (user)Ledger APIAuthorization serverClose assistant (agent)Controller (user)draft the month-end entriesrequest a token for the ledger, acting for the usercheck may_act: is this agent allowed to act for this user?token: sub = user, act.sub = agent, narrow scopePOST /journal-entries (draft)log both subjects from the token201 created, draft awaiting a human

Figure 2 — With a nested actor claim, the ledger's own log names the agent and the person, without the agent ever holding the person's credential.

Compare that to what a client-credentials token can say. RFC 9068, the JWT profile for OAuth access tokens, requires iss, exp, aud, sub, client_id, iat and jti, and specifies that where a resource owner is involved the sub "SHOULD correspond to the subject identifier of the resource owner", while for grants without one it "SHOULD correspond to an identifier the authorization server uses to indicate the client application". So a machine-to-machine token's subject is the application. It is a correct token and an honest one, and it cannot answer "on whose behalf", because there is no field holding a second party.

The shape of the credential decides which questions your logs can answer. One subject means one answer. If you want "which agent, for which person", the token has to carry two, whether through a nested actor claim or an identity pair your gateway records itself.

Distinct Identity, Preserved Context

A better model gives each agent its own identity and credentials while preserving the human context when it acts for someone. These ideas work together rather than against each other.

The agent has an identity of its own, so its actions are never mistaken for a person's. When it acts for a user, pulling a record that user is entitled to see, or submitting a request the user initiated, the user context travels with the action. The result is a record that can express the full picture:

A specific agent performed a specific action for a specific user, under a specific permission, at a specific time.

An HR onboarding agent provisioning a new hire's accounts should appear as the onboarding agent, acting for the HR coordinator who triggered the request, exercising a provisioning scope limited to onboarding. None of that survives if the agent logs in as the coordinator. All of it survives when identity and context are separate but linked.

OWASP's guidance on excessive agency lands in the same place from the security side, recommending that extensions "designed to perform operations in the context of an individual user" authenticate as that user rather than through a generic high-privileged account. Regulation is heading there too: EU AI Act Article 12 requires high-risk systems to "technically allow for the automatic recording of events (logs) over the lifetime of the system", and Article 26(6) makes deployers keep those logs for "at least six months". A log that cannot name the actor satisfies the letter of that and none of the purpose.

Scoping Agents Differently

Agent credentials should be scoped on a different basis than human ones. A person holds broad access because of a job: a procurement manager can view every supplier contract, raise purchase orders and approve invoices below a threshold, because the role demands range and judgment.

An agent has no comparable need for breadth. It exists to perform a workflow, so its credential should grant only what that workflow requires.

Take a hypothetical procurement intake agent that drafts purchase orders from approved requisitions. It does not need to read every contract or approve invoices. It needs to read the requisition, look up the vendor, and create a draft for a human to approve. Scope the credential to that and a prompt injection cannot escalate into approving payments, because the credential never carried that power.

  • Human scope follows the role. Broad by design, because people handle the exceptions.
  • Agent scope follows the workflow. Narrow by design, because the agent does one defined job.

NIST's zero-trust guidance supplies the enforcement rule that makes narrow scope hold up: access is granted "on a per-session basis" with "the least privileges needed to complete the task", and "authentication and authorization to one resource will not automatically grant access to a different resource". The last clause is the one broad service accounts violate by construction.

A Different Lifecycle

Agent credentials need different lifecycle controls, and this is where a reused human credential quietly becomes a liability.

When an employee leaves, familiar machinery runs. HR records a termination, the identity system disables the account, downstream entitlements fall away. The trigger is a person walking out.

When an agent is retired, nobody leaves. A hypothetical log-triage agent replaced by a newer version generates no HR event, and its keys do not expire on their own. Google is explicit that service-account keys "don't have an expiry time and stay valid until you delete them". If the credential is its own managed object it can be revoked when the agent is decommissioned. If it was a borrowed login or a shared account, nothing says this access existed only for that agent, so it lingers as an unowned set of keys with real reach.

The platforms that solved this bound the credential's life to the workload's. Azure's system-assigned managed identity has a "shared life cycle with the Azure resource that the managed identity is created with", and "when the parent resource is deleted, the managed identity is deleted as well". The user-assigned variant has an "independent life cycle" and "must be explicitly deleted", which is a fair trade as long as somebody owns the deleting.

Revoking is also not the same everywhere at once. Killing a credential in your own system stops your systems from presenting it. If the underlying token was ever handed to a downstream service directly, that service may keep honouring it until something tells it to stop. AWS makes the point concrete: temporary credentials are "valid until they expire", and the way to stop them early is to deny the principal, because permissions "are evaluated each time they are used". Treat "revoked here" and "revoked everywhere" as two claims, and only assert the second when you have checked.

An agent credential needs its own birth and its own death. Provisioned when the agent is registered, revoked when the agent is retired, independent of any person's lifecycle.

In Practice

An agent credential carries four traits a borrowed or shared one cannot.

  • Unique to the agent. One credential, one agent, so every action is attributable.
  • Scoped to the workflow. Permissions match the task, not a comparable human role.
  • Context-aware. When acting for a user, the user's identity rides alongside the agent's.
  • Independently revocable. Killable when the agent retires, without touching anyone's access.

The gap shows up in the record. Compare what each model can prove about the same warehouse query:

# Shared service account -- attribution lost
actor: svc-analytics
action: DELETE FROM prod.orders WHERE ...
on_behalf_of: (unknown)
permission: (account-level, unscoped)

# Distinct agent identity -- attribution preserved
actor: agent:anomaly-detector-v3
action: DELETE FROM prod.orders WHERE ...
on_behalf_of: user:dpatel@enterprise (data-eng)
permission: analytics.read-only   # <- denied: write not in scope
result: blocked

The same scoping that makes the action attributable makes it preventable. With a workflow-scoped, read-only credential the destructive query never executes. Had it executed, the record would still name the agent, the person behind it, and the scope that should have applied.

Frequently Asked Questions

Can an AI agent just use my API key? It can, and it then has every right the key has, recorded as yours. RFC 8693 calls this impersonation, where the actor is "indistinguishable from B in that context". If the key is a Google service-account key, remember it "doesn't have an expiry time" and stays valid until deleted, so the exposure has no natural end.

What is the difference between a service account and an agent identity? A service account is one credential holder; an agent identity is one actor. They coincide only when exactly one agent uses the account. Azure's user-assigned managed identities are explicitly shareable across resources, which is useful for one workload on many machines and wrong for many agents on one identity.

How do I log which user an agent acted for? Carry two subjects. The standards-based route is RFC 8693's act claim, which exists "to express that delegation has occurred and identify the acting party", with may_act controlling who is allowed to act for whom. The pragmatic route is recording the pair at your gateway. Either works; a single sub does not.

Do agents need multi-factor authentication? The concept does not transfer. NIST SP 800-63B's AAL2 requires "two distinct authentication factors" from a claimant who possesses and controls an authenticator, and an agent possesses nothing in that sense. The equivalent protection is a short-lived credential bound to a verified workload, which is what SPIFFE SVIDs and managed identities provide.

How long should an agent credential live? Shorter than you think, and ideally not your decision. AWS temporary credentials span 15 minutes to 36 hours with a 12-hour default. SPIFFE keys are "short lived, rotated frequently and automatically". Anything with no expiry is a standing grant wearing a credential's clothes.

What breaks first when agents share one credential? Attribution, then containment. You cannot say which agent acted, and you cannot revoke one without breaking the rest, so the incident response for a single misbehaving agent becomes an outage for every agent on that account.

The Principle: Separation

The thread running through all of this is separation. Agent identity separate from human identity. Agent scope separate from human scope. Agent lifecycle separate from human lifecycle.

In plain terms: agents should not silently borrow human access, and they should not hide behind shared credentials. They should have managed, scoped, auditable credentials of their own, provisioned deliberately and revoked when the job is done. Everything downstream, storage, rotation, monitoring, only works once that is in place.

For completeness, this is the model our own stack implements. Credentials are held per user in a vault and attached server-side at the moment of the call, so the agent never handles the secret, and every request carries two identities, the agent and the acting user, into the audit record. An agent's effective tool list is the intersection of the user's scopes and the agent's, which means widening one side alone changes nothing.

An agent with its own credential is an actor you can see, scope and stop. That is what makes agent activity governable, not a claim that the agent is safe, but a record that proves what it did and the boundaries it worked within.

Sources

All URLs read 2026-09-29.