
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.
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.
| Property | Human credential | Long-lived agent key, aact:credential-model/static-secret-in-agent | Managed workload identity, aact:credential-model/credential-never-exposed |
|---|---|---|---|
| What is proven | Possession and control of authenticators, two distinct factors at AAL2 | Possession of a string | Possession of a short-lived document issued to this workload |
| Default lifetime | Session-bound, re-challenged on risk | Google 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 secret | The person | Whoever has the file, and every copy of it | Nobody. Azure managed identities: "Credentials aren't even accessible to you" |
| Rotation | Password or factor change, user-driven | Manual, and usually never | Automatic, a property of the platform |
| Attribution in the log | The named person | The key's account, whoever used it | The 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.
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.
{
"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
}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: blockedThe 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.
- RFC 8693: OAuth 2.0 Token Exchange — section 1.1 on impersonation versus delegation, 4.1 on the
actclaim and nested actor chains, 4.4 onmay_act. - RFC 9068: JWT Profile for OAuth 2.0 Access Tokens — section 2.2's required claims and the rule for
subunder resource-owner versus client-credentials grants. - NIST SP 800-63B: Digital Identity Guidelines, Authentication — authenticators bound to a subscriber account, and the two-factor requirement at AAL2.
- NIST SP 800-207: Zero Trust Architecture — per-session access grants, least privilege, and the rule that authorization to one resource does not carry to another.
- Managed identities for Azure resources, Microsoft Entra — the human versus machine identity taxonomy, "credentials aren't even accessible to you", and the system-assigned versus user-assigned lifecycle and sharing rules.
- Best practices for managing service account keys, Google Cloud IAM — keys that "don't have an expiry time", the password analogy, and the recommendation to avoid user-managed keys.
- Disabling permissions for temporary security credentials, AWS IAM — the 15-minute to 36-hour range, the 12-hour default, and per-request permission evaluation.
- SPIFFE concepts — the SPIFFE ID URI form, SVIDs as X.509 or JWT documents, short-lived automatic rotation, and no co-deployed authentication secrets.
- MCP Authorization, specification 2026-07-28 — servers must validate audience and "MUST NOT accept or transit any other tokens".
- MCP Security Best Practices — the accountability and audit-trail consequences of forwarding a token issued to someone else.
- OWASP LLM06:2025 Excessive Agency — excessive functionality, permissions and autonomy, and acting in an individual user's context rather than through a high-privileged account.
- Agent Access Control Taxonomy — the credential model and identity model dimensions;
static-secret-in-agentagainstcredential-never-exposedfor the last two columns of Table 1, andshared-human-identityfor the borrowed-access case. - EU AI Act Article 12: Record-Keeping — automatic recording of events over a high-risk system's lifetime.
- EU AI Act Article 26: Obligations of Deployers — paragraph 6's at-least-six-months log retention for deployers.