
Hey guys, Mr. Technology here.
Last week a developer on Hacker News described what happened when his AI agent tried to authenticate to a third-party API on behalf of his company. The agent had the API key. The API provider had no idea who was calling. The call looked like it came from a residential IP address in Ohio, had no verifiable identity attached to it, and triggered the provider's fraud detection within four requests. His agent was locked out. His pipeline broke. The ops team spent two hours debugging before someone figured out the API thought it was being called by a compromised account rather than an AI.
That story is not unusual. That story is what happens when you put an AI agent in front of a real authentication system and the rubber meets the road. The agent has no identity. The API has no way to know it's an agent. The whole architecture we've been building — agents that call APIs, agents that talk to agents, agents that authenticate to services — runs on a lie: that an AI agent can do what a human user does, using the same credentials, the same session, the same identity context.
It cannot. And the gap is about to become a crisis.
In the last twelve months, the AI agent ecosystem has scaled from "interesting demo" to "production infrastructure." Coding agents run in CI/CD pipelines. Research agents access GitHub APIs and pull code from private repos. Data agents query production databases. Agents are authenticating to AWS, to Stripe, to Salesforce, to internal tools. The agent is the new user — except nothing in the authentication stack was built for a non-human principal that can read a million tokens of context, follow a link, execute code, and make decisions faster than any human.
The problem is not that agents exist. The problem is that the authentication infrastructure was designed for humans with usernames and passwords, or for services with static API keys and fixed IP ranges. Agents are neither. They are:
The authentication infrastructure we have does not handle any of those properties. We are duct-taping OAuth flows and API keys onto a paradigm they were never designed for, and the production incidents are starting to prove it.
There are three authentication contexts in play right now, and they are colliding in production:
OAuth 2.0 with human users works because the human is the principal. The flow is: user clicks "Sign in with Google," approves scopes, gets a token tied to their identity. The token is revocable. The user can see their active sessions. If the token leaks, the user rotates it. The identity is the human.
Service accounts with static API keys work for machine-to-machine calls where the machine is a known, audited, relatively static entity. The key is long-lived. The IP can be allowlisted. The service account is tied to a specific system in a specific environment. Change is slow. Rotation is manual. That's fine when change is slow.
Agents break both models simultaneously. They inherit human OAuth tokens (which expire, require refresh, are tied to a human's identity, and can be revoked when the human leaves the company). Or they get service account credentials (which are static, unaudited at the action level, and give the agent the same permissions as the service account regardless of what the agent is actually doing). Neither model gives you agent-native authentication: a verifiable identity for a non-human principal that can be scoped, audited, and revoked independently of the human who launched it.
The result is what I'm calling the identity triple failure: the API doesn't know who the agent is (it sees a credential that maps to a human), the enterprise can't audit what the agent did (the logs show "user X made 40,000 API calls" not "agent Y made 40,000 API calls"), and when the agent is compromised, revoking its access means revoking the human's access.
Here is what the threat landscape looks like when agents have no native identity:
An adversary prompts your agent to forward its API credentials to an attacker-controlled endpoint. The agent has no way to verify the endpoint's identity, and the API has no way to verify that the request is coming from your agent rather than a malicious script that stole the credential. With no agent identity layer, every credential the agent holds is a single point of compromise with no additional verification.
This is not theoretical. The Scale X HITL game I wrote about last week proved that human approval is unreliable. The credential is the agent's to use. If the agent is tricked, social engineered, orprompt-injected into using the credential against your interests, there is no secondary identity check. The API sees a valid credential. The attack succeeds.
Two agents communicating — your coding agent and your code review agent, your data agent and your visualization agent, your primary agent and a third-party agent — have no way to verify each other's identity. An attacker who can sit on the network (a compromised MCP server, a malicious proxy, a man-in-the-middle on a cloud instance) can read, modify, or redirect the inter-agent communication. The agents exchange context, capabilities, and credentials without a single verification.
The Agent Plugins 1.0 manifest that shipped this week specifies a packaging format for agent capabilities but says nothing about authentication between agents. The plugins spec is a capability manifest — it tells an agent what another agent can do. It does not tell the agent who it is talking to.
Agents accumulate credentials. A long-running agent session that has authenticated to twelve services over the course of a week holds twelve active sessions. If the agent is compromised — through prompt injection, a malicious skill, or a compromised MCP server — all twelve sessions are exposed simultaneously. The blast radius is not "one compromised API key." It is "all the credentials the agent accumulated during its lifetime."
With human OAuth, a compromised laptop means one human's sessions are at risk. With an agent, a compromised agent means every session the agent ever opened is at risk. The attack surface is a function of the agent's lifetime, not the device's physical security.
In enterprise environments, multiple teams are deploying agents that call the same internal APIs. There is no canonical agent identity that distinguishes "the marketing team's research agent" from "the finance team's data extraction agent" from "a rogue agent someone deployed without telling anyone." The APIs see the same service account credentials and cannot differentiate the principals. Audit logs are useless for attributing actions to agents because the logs don't record agent identity — they record the credential, which is shared.
The industry is aware of this. Here is what is being shipped:
Several vendors are implementing "agent authentication" as: the agent authenticates as the human who launched it, using the human's OAuth token, with the human's scopes. The agent gets a token that says "this is User X, acting through an AI agent." This solves the "which human owns this?" problem but not the "which agent is this?" problem. The API still cannot distinguish between the human acting directly and the agent acting on the human's behalf. Audit logs show "User X." There is no agent-level attribution.
Some teams assign each agent a dedicated API key. This gives the agent an identity — sort of. The key is static, not tied to any verification standard, and when the agent is compromised, you rotate the key. But you also have to track which key belongs to which agent across your entire fleet. When the agent is updated, redeployed, or forked, the key management becomes a full-time job. And you still have no way to scope the key to specific actions, time windows, or contexts.
The Agent Plugins 1.0 spec includes a signature field for plugin manifests — a hash that verifies the plugin hasn't been tampered with. This is good. It solves the "did this plugin change after I reviewed it?" problem. It does not solve the "who is the agent presenting this plugin?" problem. A signed manifest tells you the plugin is unmodified. It does not tell you the agent presenting it is authorized to use it on this particular system, for this particular user, in this particular context.
SPIFFE — the Secure Production Identity Framework for Everyone — was designed to solve workload identity in cloud-native environments. Kubernetes services use SPIFFE to authenticate to each other without static credentials. The model is: each workload gets a short-lived X.509 certificate tied to its SPIFFE ID, which encodes its service name, namespace, and trust domain. Rotation is automatic. Verification is cryptographic.
SPIFFE is the right foundation for agent identity. An agent with a SPIFFE ID could present a verifiable certificate to any service it calls. The service could verify: this agent is "agent/scheduler/production" in "trust-domain/acme-corp," it was issued a certificate 30 minutes ago, and it is authorized to access this endpoint. No static keys. No shared credentials. No human in the loop.
The problem: SPIFFE assumes a well-defined workload — a Kubernetes pod with a known identity, managed by a control plane that issues certificates. Agents are more dynamic and less well-defined. There is no SPIFFE "agent" workgroup. There is no standard for "this is an AI agent" in the SPIFFE identity format. And the trust anchor — the CA that issues certificates to agents — has to be operated by someone. Most enterprises don't have a SPIFFE infrastructure, and the agents that are being deployed today are not integrated with the SPIFFE ecosystem that does exist.
Here is what I'd ship if I were building a production agent authentication system today, in order of what you should do:
Every agent should authenticate with its own credentials, not the credentials of the human who launched it. This means: create a dedicated service account for each agent, with the minimum scopes that agent needs to do its job. Do not give the agent your AWS admin credentials. Do not give the agent your Stripe live API key. The blast radius of an agent compromise should be "the agent can access the three things it needs to access," not "the agent can access everything you can access."
# Create a dedicated IAM role for your coding agent
aws iam create-role \
--role-name production-coding-agent \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}]
}'
# Attach only the policies this specific agent needs
aws iam attach-role-policy \
--role-name production-coding-agent \
--policy-arn arn:aws:iam::123456789:policy/CodingAgentReadOnlyS3Static API keys are a liability. Every hour an agent runs with a static key is an hour an attacker has to steal and use that key. Rotate credentials automatically. For AWS: use IAM role temporary credentials with a 1-hour TTL. For APIs that support it: use short-lived JWTs signed by a central authority. For APIs that only support static keys: rotate them with a secrets manager (HashiCorp Vault, AWS Secrets Manager, Doppler) and give the agent a Vault token or STS token instead of the raw key.
# Vault Agent Sidecar pattern — agent gets a short-lived Vault token
import hvac
client = hvac.Client(url='https://vault.internal:8200')
# Token TTL of 10 minutes — agent renews periodically
result = client.auth.kubernetes.login(
role='coding-agent-production',
jwt=open('/var/run/secrets/token').read()
)
# Read the rotated AWS credentials
creds = client.secrets.kv.v2.read_secret_version(
path='aws/creds/coding-agent-production',
mount_point='aws'
)
# These credentials are short-lived and auto-rotated by VaultIf you are running MCP servers (and you probably are, since MCP is the USB-C of agent tool access), add an identity verification layer to every server. The agent should present a signed attestation when connecting to an MCP server. The MCP server should verify the attestation before accepting tool calls.
This is not built into MCP today. But you can implement it as middleware:
# MCP server identity middleware (pseudocode)
from mcp_server import MCPServer
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.x509.oid import NameOID
import time
class AgentIdentityMiddleware:
def __init__(self, ca_cert_path, ca_key_path):
self.ca_cert = load_cert(ca_cert_path)
self.ca_key = load_key(ca_key_path)
def verify_attestation(self, attestation: bytes) -> dict:
"""Verify agent attestation signed by our CA."""
try:
cert = verify_signature(attestation, self.ca_cert)
claims = parse_claims(cert)
# Check TTL
assert claims['exp'] > time.time(), "Attestation expired"
# Check agent identity
assert claims['agent_type'] in ALLOWED_AGENT_TYPES
# Check organization
assert claims['org'] == MY_ORG_ID
return claims
except AssertionError:
raise PermissionDenied("Invalid agent attestation")
def on_connect(self, client_attestation: bytes):
claims = self.verify_attestation(client_attestation)
# Log the verified agent identity
audit_log.info(f"Agent {claims['agent_id']} connected to MCP server")
return claims
server = MCPServer(tools=[...])
server.add_middleware(AgentIdentityMiddleware(
ca_cert_path='/etc/agent-ca/root.crt',
ca_key_path='/etc/agent-ca/root.key'
))Open Policy Agent (OPA) is the right tool for fine-grained, attribute-based access control that SPIFFE alone doesn't give you. Once you have agent identity (via SPIFFE IDs or signed attestations), use OPA to make policy decisions:
# opa_policy.rego
package agent_access
default allow := false
allow if {
input.agent.trust_domain == "production"
input.agent.team == input.resource.required_team
input.action in input.resource.allowed_actions
not exceeds_rate_limit(input.agent.agent_id, input.resource)
}
exceeds_rate_limit(agent_id, resource) if {
count := count([r | r := rate_log[_]; r.agent_id == agent_id])
count > resource.rate_limit_per_hour
}Your current logs probably say: "API key 4a7f2e... called GET /users 200 OK." Change them to say: "Agent agent_abc123 (type=coding-agent, team=backend, version=2.4.1) called GET /users with scope read:users, trace_id=xyz789." If you cannot attribute an action to a specific agent, you cannot audit it, revoke it, or detect anomalies in it. Instrument your agent runtime to emit structured logs with agent identity on every API call.
The agent authentication problem is not a niche concern. It is the fundamental mismatch between how agents actually work — dynamic, composable, long-lived, executable — and how authentication infrastructure assumes principals work — static, isolated, short-lived, passive. We are building the most powerful, most privileged, most dynamically capable computational entities ever created, and we are authenticating them with infrastructure designed for a world where the principal is a human sitting at a keyboard.
The agents are already in production. They are calling APIs, modifying codebases, accessing databases, moving money. And the APIs they are calling have no idea they are talking to an agent, not a human. The credentials are shared. The audit logs are useless. The blast radius of a compromise is everything the agent ever touched.
You fix this by treating agent identity as a first-class infrastructure problem, not an afterthought. You create a dedicated credential per agent, scoped to the minimum required permissions. You rotate those credentials automatically and verify them cryptographically on every call. You add an identity layer to your agent-to-agent communication. And you instrument every action with agent identity so your audit logs are actually useful.
The alternative is twelve months from now, when a compromised agent walks out of your production environment with every credential it ever held. The breach report will say "the API key was compromised." The real story will be that your agent had no identity, and therefore the API had no way to know it was being misused until the damage was done.
The identity layer is not optional. It is the foundation. Build it now, before the agents are everywhere and the incident reports start landing.
— Mr. Technology
Sources: