Agent Authentication: Your Agent Should Not Be You

In a workshop room at AI Engineer, Paola Estefanía de Campos asked how many people in the audience used an AI agent every day. Most hands went up. Then she asked how many gave that agent access to their Gmail, their calendar, their personal accounts. Far fewer hands.
The gap between those two shows is the subject of this post. It is not squeamishness. It is that for most services, the only way to connect an agent is to hand it a credential that makes it indistinguishable from you. As she put it: the agent is acting on behalf of us but pretending to be us. You would never give a new hire the CEO's login and tell them to go read the email — and yet that is the shape of nearly every agent integration shipping today.
TL;DR — Agent authentication means giving an AI agent its own identity and narrow, revocable authority rather than letting it borrow yours. An agent holding your API key or your OAuth token appears in every log as you, which leaves you with no visibility, no scoping, and no way to fire one agent without disconnecting the whole integration. The Agent Auth Protocol draft proposes three fixes: discoverable capabilities, per-capability grants that carry constraints, and a keypair per agent. MDflow already publishes the discovery half of that — a machine-readable capability inventory plus OAuth metadata — and issues one revocable token per agent.
What is agent authentication?
Agent authentication is the practice of giving an AI agent its own verifiable identity at the moment it calls a service, instead of letting it present a credential that belongs to a human. The point is not extra ceremony. It is that three things become possible that are impossible today: the service can tell which agent is calling, the agent can hold less authority than the person who delegated it, and a single agent can be revoked without tearing down everything else connected to the same account.
The Agent Auth Protocol draft is the most concrete attempt at this so far, and it frames the root cause precisely: agents today do not have identity. Identity needs two properties — continuity, so the thing persists across calls, and distinctiveness, so it can be told apart from everything else. An OAuth token gives continuity without distinctiveness; it persists, but it is yours. An API key gives distinctiveness without continuity; it is its own thing, but it has no lifecycle, no owner relationship, and no story about who delegated what.
The draft names three failures of the borrow-the-user's-credential model:
- No visibility. Every log line says the user did it.
- No scoping. The credential carries every permission it was issued with, and a helpful agent will use all of them.
- No isolation. Two agents sharing one token share one blast radius.
There is a fourth problem for agents with no human behind them at all. For an autonomous agent, the draft notes, there is no identity model, so the agent is forced to pretend to be a human user — opening a browser, solving a CAPTCHA. We have built an entire category of software whose authentication story is impersonation.
The workshop organised the solution around three questions, and they map onto the spec cleanly: how does an agent discover what it can do, how is it authorized to do a specific thing, and how does it prove who it is?
Discovery: the phone book problem
Today, an agent knows what it can do because a human wired it up by hand. Every MCP server in your config is a human decision, typed once and then forgotten. The workshop's analogy was a phone book: a place you look up who you can call and how to call them, rather than being told each number in advance.
The protocol's version of that is an unauthenticated metadata document at /.well-known/agent-configuration, which any service can publish:
{
"version": "1.0-draft",
"provider_name": "Example Service",
"issuer": "https://api.example.com",
"algorithms": ["Ed25519"],
"modes": ["delegated", "autonomous"],
"approval_methods": ["device_authorization", "ciba"],
"endpoints": {
"register": "/agent/register",
"capabilities": "/capability/list",
"execute": "/capability/execute",
"request_capability": "/agent/request-capability",
"revoke": "/agent/revoke"
},
"jwks_uri": "https://api.example.com/.well-known/jwks.json"
}
If you have implemented OpenID Connect discovery, this will look familiar on purpose. Above it sits a directory — a searchable index that maps an intent ("bring me my last email") to the capabilities that could satisfy it, queried as GET {directory_url}/api/search?intent={intent}. The workshop was blunt that this directory is a bridge, not the destination: until services publish their own metadata, you populate it by translating an existing OpenAPI description into capabilities, which is also how the spec's adapters work.
Capabilities, not scopes
The second idea is the one most likely to change how you design an API. A capability, in the draft's words, is a server-offered action described by a stable identifier and a human-readable description — lowercase and underscores only, [a-z0-9_]+ — with JSON Schema for its input and output. It is the unit of authorization.
Contrast that with a scope. When you grant an agent read on a mailbox, you have said almost nothing: read which messages, how many, for how long? The workshop's point was that read is a big thing, and a capability is cut down to one action you can actually reason about.
A grant is then one capability to one agent, and it can carry constraints keyed to the input fields of that capability:
{
"agent_id": "agt_01H...",
"capability": "transfer_funds",
"status": "active",
"constraints": {
"amount": { "min": 0, "max": 1000 },
"currency": "EUR"
},
"expires_at": "2026-11-01T00:00:00Z"
}
Constraints are additive, and the asymmetry matters: a server may narrow them, but must not widen them beyond what the agent requested without a new approval. A violation comes back as a 403 constraint_violated with a violations array, and a capability the agent never got comes back as 403 capability_not_granted. The grant also expires on its own clock, independent of the agent's.
Identity: a keypair per agent
The third piece is what makes the first two auditable. Each agent gets an Ed25519 keypair, and the private key must never be sent to the server. The agent signs its own short-lived JWTs — typ: agent+jwt, which the draft says should expire within 60 seconds, with duplicate jti values rejected inside that window to kill replay. Proof of possession via DPoP or mTLS is available on top.
The data model has three records, and the middle one is the useful novelty:
- A host is the persistent identity of the client environment where agents run — your Claude install, your Cursor, your CI runner. The spec is careful that it is an identity record and credential set, not a running process.
- An agent is a runtime actor scoped to one conversation, task, or session, registered under a host, with its own identity.
- A user is the person the agent acts for. A delegated host links to at most one user; an autonomous host may have none.
That split is what turns "someone accessed your mailbox" into "the email-reader agent, running under the Claude host, on behalf of this user, at this timestamp." And because the agent is now a named thing, you can do the thing you cannot do with a shared token: revoke it alone. Revocation is permanent, cascades from host to agents, and the draft is honest that it takes effect only within a bounded window — delayed by at most the cache TTL, with 5 to 30 seconds suggested. Agents also age out on three separate clocks: a session TTL from the last request, a max lifetime from the last activation, and an absolute lifetime from creation, after which the agent is permanently revoked.
For the human approval itself, the draft leans on existing standards rather than inventing a flow: RFC 8628 device authorization is the mandatory baseline, with OpenID CIBA as the backchannel option. And it includes the caveat that every honest version of this design has to include — an agent that controls the user's browser could approve its own device flow, so for state-changing capabilities the spec requires WebAuthn with user verification, or CIBA on a channel the agent cannot reach.
Two pieces of honesty about status are worth stating plainly. First, the protocol deliberately does not define logging formats or audit schemas; it only says servers should log the agent ID, capability name, timestamp, and result. Traceability comes from the JWT claims, and the rest is yours to build. Second, this is a v1.0 draft, published and maintained by the Better Auth team and open source, but not on an IETF or W3C standards track. It sits alongside other proposals — AAuth, and the IETF "On-Behalf-Of" draft that extends OAuth with an act claim — and it is explicitly not a replacement for OAuth. A server may accept both OAuth bearer tokens and Agent Auth JWTs, and MCP is treated as an implementation layer that can carry capabilities through an adapter. Nobody should rewrite their auth stack on a draft. The problem it describes, though, is not speculative at all.
Why agent authentication is useful
For developers
Three things you cannot do today become routine. You can attribute a call to a specific agent, which is the difference between an incident review that produces an answer and one that produces a shrug. You can grant less than you hold, so the agent summarising your inbox cannot send from it — and a prompt injection that lands in a document cannot reach a capability that was never granted. And you can fire one agent, instead of rotating a credential and breaking four integrations that happened to share it.
There is a real cost, and the workshop named it rather than hiding it: approval fatigue. If every read needs a tap, people will hate you — the phrase used was wanting to be safe but not annoying. The design answer is defaults per host (read-only unless escalated) and policies per user, so that interactive approval is reserved for the actions that change something.
For AI agents
An agent that can enumerate its own capabilities stops guessing. Today a model infers what a tool will allow from a description and finds out by failing, often ambiguously. With a capability list and machine-readable denials — capability_not_granted, constraint_violated with the specific violations — the failure is legible, and the agent can request the capability it needs and wait for approval instead of improvising a workaround. That request-and-wait path, rather than silent retry, is the behaviour you actually want; the draft even forbids automatically retrying a denied registration.
For autonomous agents, the payoff is bigger: they stop having to impersonate a human to get in the door. An agent that can hold its own identity does not need to drive a browser and defeat a CAPTCHA to prove it exists.
Which applications benefit most
- Mailboxes and calendars — the canonical case, and the one the workshop demoed. Reading is low-risk and sending is not, so the two belong behind different grants.
- Payments and anything that moves money — the place where a constraint like
amount: { max: 1000 }stops being a nice abstraction and starts being the control. - Code and infrastructure — where a single over-scoped token is the difference between an agent that opens a pull request and one that can deploy.
- Customer and CRM data — where attribution is a compliance requirement, not a convenience, and "a user exported the table" is not an acceptable log line.
- Knowledge bases and document stores — where agents now read and write, and where you need to know which agent changed a document, and be able to see exactly what it changed.
How MDflow fits
MDflow is on the receiving end of this problem. It is a markdown workspace that agents connect to, so the question is not what credential we hand an agent but what we accept from one, and what we can tell you afterwards.
What already lines up today. The discovery half is built. MDflow publishes its capability inventory for machines: /llms.txt as the map, /docs.md as a self-contained agent guide, /openapi.json as the formal spec of every REST operation, and an agent card at /.well-known/agent-card.json. Alongside that sits the authorization metadata: RFC 9728 protected-resource metadata at /.well-known/oauth-protected-resource, which tells a client which authorization server governs the MCP endpoint it is calling. A client can therefore discover both what MDflow does and how to get authorized without a human pasting anything — and because that inventory is already machine-readable, the OpenAPI-to-capability translation the protocol relies on has something real to read.
The credential is already not your password. Claude and ChatGPT connect to MDflow's MCP server through a browser OAuth flow: you click connect and authorize, and the agent receives a token. MDflow validates that token and rejects browser-session JWTs outright, so only deliberate OAuth grants and Personal Access Tokens reach the API at all.
And each agent can already have its own revocation handle. Personal Access Tokens are named, stored only as a SHA-256 hash, individually revocable, and carry a last-used timestamp — so "one token per agent, revoke the one that misbehaved" is a workflow you can adopt today, which is the practical core of what the protocol formalises. Team accounts go further: writes through a Team token are attributed to the token creator in the Team audit feed.
One detail is worth more than it looks. Document version history in MDflow is private to the owner and is deliberately not exposed through the HTTP API or the MCP server. An agent can rewrite a document; it cannot read, edit, or erase the record of what it changed. The audit surface sits outside the agent's reach by design, and the activity view shows a side-panel diff of exactly what each edit did.
Where we fall short, stated plainly. A Personal Access Token is read and write, and it is account-wide: it sees every workspace, not the one you had in mind. There are no per-capability grants, no constraints, no per-call capability log on personal accounts, and no per-agent keypair — the token identifies a credential, not an agent with its own identity. Measured against the draft, MDflow has discovery and revocation, and does not yet have capabilities or cryptographic agent identity.
Where we are headed — direction, not a dated commitment. The next useful steps are narrowing what a token carries rather than rebuilding how tokens work: read-only tokens, workspace-scoped tokens, and per-token activity you can read. If one of the competing drafts wins, MDflow already has the two prerequisites for speaking it — a machine-readable capability inventory and an OAuth-based grant — so adopting capability grants would be a narrowing exercise, not a new auth model. We would rather ship the narrow token than guess which draft wins.
The bottom line
The sentence to take from the workshop is this: stop giving agents your credentials, give them authority. Stop saying pretend to be me and start saying act for me, within these limits. The three pieces that make that sentence implementable are discovery, capability grants, and an identity per agent — and the first one is cheap enough that any service can ship it now.
If your agent reads and writes documents, the same logic applies to your notes. Start free, connect an AI agent over MCP, or read the API docs and see what MDflow tells a machine about itself before it ever asks for a token.
Frequently asked questions
What is agent authentication?
Agent authentication is giving an AI agent its own verifiable identity when it calls a service, instead of letting it present a credential that belongs to you. In practice it means three things today's setups do not provide: the service can tell which agent is calling, the agent holds less authority than the person who delegated it, and that one agent can be revoked without disconnecting everything else.
Why is giving an agent your API key or OAuth token a problem?
Because the agent then becomes indistinguishable from you. The Agent Auth Protocol draft names three failures of that model: no visibility, because every log line shows your identity rather than the agent's; no scoping, because the credential carries every permission it was issued with; and no isolation, because two agents sharing your token share one blast radius. Revoking means disconnecting the whole integration rather than firing one agent.
What is the Agent Auth Protocol?
The Agent Auth Protocol is a v1.0 working draft by Paola Estefanía de Campos and Bereket Engida, maintained by the Better Auth team, that defines how a service advertises what agents can do, how an agent is granted individual capabilities, and how an agent proves its own identity with an Ed25519 keypair and short-lived signed JWTs. It is open source and explicitly not a replacement for OAuth: a server may accept both OAuth bearer tokens and Agent Auth JWTs. It is a draft, not an IETF or W3C standards-track specification.
How is a capability different from an OAuth scope?
A scope like read covers everything a service considers reading. A capability is one named action with a JSON Schema for its input and output, and a grant of that capability can carry constraints keyed to the input fields, such as a maximum amount or an allowed list of recipients. A server may narrow those constraints later but must not widen them without a new approval, so the grant stays as small as the task that justified it.
How does MDflow authenticate AI agents today?
MDflow publishes its capability inventory and its authorization metadata for machines to read, at /llms.txt, /docs.md, /openapi.json, an agent card, and RFC 9728 protected-resource metadata, so a client can discover both what MDflow does and where to get a token without a human pasting anything. Claude and ChatGPT connect over a browser OAuth flow and never see your password; scripts and other agents use named Personal Access Tokens that are hashed at rest and revocable one at a time, so each agent gets its own revocation handle. A token is still read and write across the owner's whole account, and MDflow has no per-capability grants yet.
Further reading
- Agent Auth Protocol — specification v1.0-draft — the primary source for every endpoint, claim, and error code quoted above.
- Agent Auth Protocol homepage — the problem framing and the FAQ on how it relates to OAuth and MCP.
- Full Workshop: Agent Auth Protocol — Paola Estefanía de Campos, Better Auth — the AI Engineer workshop this post is built on.
- RFC 8628: OAuth 2.0 Device Authorization Grant — the baseline approval flow.
- RFC 9449: OAuth 2.0 Demonstrating Proof of Possession (DPoP) — the optional binding that stops a stolen token being replayed.
- RFC 9728: OAuth 2.0 Protected Resource Metadata — the discovery document MDflow already serves.
- Agent Authorization: Why an API Key Is the Wrong Credential — the companion piece on OAuth token exchange and per-call minting.
- MDflow MCP docs and HTTP API docs — what MDflow accepts from an agent today.