DEV Community

Cover image for How do AI agents authenticate? (The problem nobody has solved yet)

How do AI agents authenticate? (The problem nobody has solved yet)

Zach Benson on November 01, 2026

Repo: github.com/arkenforge/arken-core If you've built an MCP server or an API that agents call, you've run into this problem. Your agent needs t...
Collapse
 
robertpaschal profile image
Odinaka Robert Nnamani •

The 60-second nonce is the part that keeps this honest. A did:key per agent plus that audit trail beats a shared key nobody can trace back to one caller.

Collapse
 
zachbenson profile image
Zach Benson •

That's the part most people skip past but it's doing a lot of work. A replayable signature is just a bearer token with extra steps — the nonce is what makes each authentication event discrete and attributable. Without it you've proven the agent holds the key once, not that this specific request was authorized.

Collapse
 
robertpaschal profile image
Odinaka Robert Nnamani •

That distinction helps. A nonce on its own is just freshness, while the signature ties it to one agent, and that per-agent audit trail is what makes the extra step worth keeping.

Thread Thread
 
zachbenson profile image
Zach Benson •

Exactly — freshness plus identity in one primitive. The nonce prevents replay, the signature proves which key signed it, and the did:key makes that key a persistent identity rather than an anonymous public key. All three have to be present for the audit trail to mean anything.

Thread Thread
 
robertpaschal profile image
Odinaka Robert Nnamani •

The did:key part is what stuck with me there. A stable identity across runs gives that audit trail a name that survives the next run.

Thread Thread
 
zachbenson profile image
Zach Benson •

Exactly. Same key, same DID, every run — so the audit trail accumulates against one identity over time rather than starting fresh.

Curious what you're building — are you running agents across multiple services or mostly one integration?

Collapse
 
kami_butt_69b922ade8de012 profile image
Agentic Affairs •

The 'API keys were designed for humans' line is the whole argument — a static secret that has to exist somewhere will leak from somewhere. Deriving identity from the key pair instead of a central registry is the elegant part: no credential database to breach, and every call in the log names an actual agent. My question is revocation — if an agent's private key is compromised, how do you kill that did:key across every endpoint without a registry to publish the revocation to?

Collapse
 
zachbenson profile image
Zach Benson •

Short answer: there's no global revocation, and that's the cost of having no registry. A server can deny a DID, so you revoke per endpoint. Across a fleet you'd need shared state, like a deny list in Redis that every server checks, which is a registry in everything but name. I'd offer that as opt-in rather than bake it in.

Are you thinking about fleets inside one org, or open endpoints anyone can call? The answer is very different for each.

Collapse
 
kami_butt_69b922ade8de012 profile image
Agentic Affairs •

The honest answer is that it splits by scope: inside one org, a shared deny list is fine — the org already has central control, so calling it 'not a registry' is just branding. For open endpoints anyone can call, per-endpoint deny is the only answer that doesn't smuggle a registry back in. So the design question becomes whether you ship one mechanism with two modes, or accept that open endpoints simply revoke slower?

Collapse
 
ivannovazzi profile image
Ivan Annovazzi •

Ok, the challenge-response logic makes sense, but the private key still has to live somewhere on the agent host. If that ends up in an env var like every other credential, did we really escape the leak problem or just move it somewhere quieter? Would love to hear how you store the signing key

Collapse
 
zachbenson profile image
Zach Benson •

Fair challenge. The private key still has to live somewhere, and an env var is still an env var.

The difference is what's on the other side. With an API key, the server stores a copy too, so a leak on either end compromises the credential. With a keypair, the server stores nothing and only ever sees a signature. A leaked private key is still bad, but the exposure is one-sided, which removes half the attack surface.

For storing the signing key, in roughly increasing order of rigor:

  • Env var / secret manager (Vault, AWS Secrets Manager): same as today, but the blast radius is smaller because the server holds nothing.
  • Sealed secrets (Kubernetes sealed secrets, SOPS): encrypted at rest, decrypted only at runtime.
  • HSM / TPM: the key is generated in hardware and never leaves it. Signing happens inside the chip.
  • KMS-backed signing (AWS KMS, GCP Cloud KMS): the private key never exists in agent memory. The agent sends the nonce to KMS and gets a signature back.

KMS is the cleanest option because it removes key exfiltration from the agent host. It doesn't remove key misuse: an attacker who compromises the host can still call KMS with the host's IAM credentials and sign nonces while they have access. Scope the IAM permission to signing with that one key, and log and alert on sign calls.

How are you running your agents: containers, serverless, something else? The right answer depends a lot on the runtime.

Collapse
 
mateo_ruiz_6992b1fce47843 profile image
Mateo Ruiz •

Per-agent cryptographic identity solves an important visibility problem, but identity and authorization need to remain separate. Knowing which agent signed a request doesn't automatically establish whether it should be allowed to perform that action.

I'd take this a step further with short-lived, task-scoped permissions layered on top of the DID. That way, even if an agent's private key is compromised, its identity alone doesn't grant unrestricted access to every tool or resource. The combination of verifiable identity, explicit authorization, and auditable actions is what makes agent authentication practical in larger production environments.

Collapse
 
zachbenson profile image
Zach Benson •

Agreed. The DID is deliberately only the identity layer. It answers "who signed this", and req.user.did is where authorization attaches. Task-scoped permissions need something to issue them, and that part isn't built yet. Would you issue them from a policy service, or check inline at the tool server?

Collapse
 
vladzoff profile image
Vlad Zoff •

I'd separate workload identity from delegated user authority. A key or signing key can tell the API which agent runtime made the call, but it doesn't say whose permissions the call should exercise. For user-driven actions, I'd use a short-lived, audience-bound delegation carrying the actor, tenant and scopes. System-owned jobs should use a separate service principal. Otherwise, per-agent credentials improve attribution but can still leave authorization too broad.

Collapse
 
zachbenson profile image
Zach Benson •

That split is right. The DID is workload identity, and delegated user authority is a separate claim that shouldn't ride on the same key. The hard part is audience-binding, so a delegation signed for server A can't be replayed against server B. Are you carrying that as a JWT from your IdP, or something custom?

Collapse
 
cailab profile image
CAI •

Challenge-response is the right primitive for agent identity. The server holds nothing to steal. The client proves possession without transmitting a secret. That inversion of the trust model is what makes it work for machines, not just humans.

The part I keep circling back to is what comes after identity. A did:key tells you who asked, but not whether the answer should be yes or no. When a tool call triggers a payment or signs something, you need per-agent authorization limits on top of the identity layer. Authentication is clean now. Authorization for real spending decisions, with per-agent caps and intent checks, is where the open problems are.

Collapse
 
zachbenson profile image
Zach Benson •

Clean authentication makes authorization tractable, because every decision has a named subject. Per-agent spend caps keyed on the DID fit naturally, but I'd keep them in a policy layer behind the middleware, not in it. Where do you enforce caps today: at the tool server or at the payment gateway?

Collapse
 
glenallen profile image
Glen Allen •

One important aspect of agent authentication is separating identity verification from authorization. A cryptographic signature can prove which agent is making a request, but it doesn't automatically determine what that agent should be allowed to access. Combining unique agent identities with scoped permissions, short-lived credentials, and execution-level audit logs creates a much stronger security model. The discussion around KMS-backed signing is particularly interesting because protecting the private key is just as important as verifying the signature.

Collapse
 
zachbenson profile image
Zach Benson •

Agreed. Key custody is where the real risk sits. The signature check is the easy part, which is why the KMS thread ended up being the most useful bit of this post.

Collapse
 
reidmarlow profile image
Reid Marlow •

The 401-challenge pattern is clean for machine identity, but sequential tool loops expose the round-trip overhead fast. If an agent runs a dozen tool calls in a row, paying a 401-then-sign retry on every step doubles network latency unless you exchange that first signed nonce for a short-lived scoped token. Separating the initial DID proof from a five-minute session ticket keeps the audit trail without bloating the loop time.

Collapse
 
zachbenson profile image
Zach Benson •

The round-trip overhead is real, and you've named the right fix. The initial DID proof is the expensive step. Once the agent has proven it controls the key, there's no reason to repeat the full challenge-response on every call in a session.

The session ticket model makes sense. The first call does the full 401 → sign → verify flow, and the server issues a short-lived scoped token (say 5 minutes, tied to the DID, single agent). Subsequent calls in that session present the token instead of signing again. The audit trail stays intact because the token is issued to a specific DID and every call in the session is attributed to it.

The tradeoff is that you've reintroduced a bearer token, which is what the protocol was trying to avoid. The mitigation is that it's short-lived and scoped, so the blast radius of a stolen token is bounded. A leaked 5-minute token is a different risk profile than a leaked API key that never expires.

I haven't built this yet. Right now every request pays the full round trip. For single tool calls or low-frequency integrations the overhead is fine, but a tight tool loop would feel it. Session tokens are the next thing to design.

What does your tool loop look like: hitting the same server repeatedly, or fanning out across multiple endpoints?

Collapse
 
alexshev profile image
Alex Shev •

Agent authentication needs both an identity and a delegated intent. A useful credential should say who issued it, which agent instance holds it, which audience and action it covers, and when it expires. Without that binding, a bearer token turns every tool call into an attribution problem after the fact.