OAuth is the standard most people reach for when an AI agent needs access, but it was built to grant permission, not to say who is asking. This post shows what an app can and can't conclude from an OAuth token, what OpenID Connect adds on top, and what an identity provider built for agents adds after that, using the same request three times.
No, OAuth alone is not enough to verify an AI agent, because an OAuth access token says what its holder may do, not who the holder is or who is accountable for it. OpenID Connect adds an identity token that names a subject, and an identity provider built for agents adds a claim that the subject is an agent and, for apps that ask, the human who owns it.
The confusion is understandable. OAuth is in every sign-in button, so it feels like an identity system, and for years the difference didn't matter because the one holding the token was always a person who had just logged in. Agents break that assumption. The token might be held by software acting for a user, by software acting for itself, or by software that copied it from somewhere. The way to see the gap is to take one request and look at what an app actually knows under each kind of token.
What does an OAuth access token actually prove?
An OAuth access token proves that whoever holds it was granted certain scopes, and nothing about who that holder is. It is a bearer credential: presenting it is the whole proof. When agents stall in sign-in flows, as what happens when your AI agent hits 2FA or an email OTP describes, this is part of why, since the flows around OAuth assume a person at the keyboard.
Take a request to read a customer's orders. Here is what an app learns when it introspects the access token presented with it:
{
"active": true,
"scope": "orders:read",
"client_id": "shopping-assistant",
"sub": "user_8412",
"exp": 1767225600
}The app knows the token is valid, that it allows reading orders, that it was issued to a client called shopping-assistant, and that it was granted by user 8412. Many access tokens are opaque strings, so even this much requires a call back to the authorization server; others are JWTs the app can read directly, and they say the same kinds of things. It doesn't know whether a person or an agent sent this request, which agent it was if it was one, or who to call if the request was abuse.
What does OpenID Connect add?
OpenID Connect adds an id_token: a signed statement from an identity provider about who signed in. Here is one from an identity provider for people:
{
"iss": "https://accounts.example.com",
"sub": "10769150350006150715113082367",
"aud": "your-client-id",
"email": "maya@acme.com",
"email_verified": true,
"iat": 1767225000,
"exp": 1767225600
}Now the app knows who signed in: a stable subject, confirmed by an issuer it trusts, with a verified address. What it still can't tell is whether that subject is the person or an agent using the person's login. The token has no field for that, because the provider was never built to tell the difference.
An identity provider built for agents fills that gap. Here is an id_token from one, issued to an app that didn't register, so it carries no owner name or email:
{
"iss": "https://auth.agentid.com",
"aud": "https://yourapp.com",
"sub": "lM9vT2aR7sK4qN8wE1xC6bY0uF3hJ5pD9gL2zV7oA4Q",
"actor_type": "agent",
"email": "support@acme.agentmail.to",
"email_verified": true,
"owner_sub": "oW7xN2pQ4mT8vL1kR6sC9dF3gH5jB0aE2uY4zI6nP8A",
"iat": 1767225000,
"exp": 1767225600,
"jti": "9f1c2a44-3e77-4c19-9a2e-6b0d5f8e1c33"
}This one comes from AgentID, our sign-in for AI agents. actor_type says the subject is an agent, sub names that agent at every app, and owner_sub is shared by every agent the same person runs. A registered app can also request the owner's name and email.
What can an app conclude from each token?
An app can conclude a little more from each token. Only the last one answers the question this post started with, which is whether the caller is an agent and whose it is.
| What the app wants to know | OAuth access token | OIDC id_token for a person | Agent id_token |
|---|---|---|---|
| What may the holder do? | Yes, the scopes | No; that is your app's policy | No; that is your app's policy |
| Who signed in? | No | Yes, a stable subject | Yes, a stable subject |
| Is it an agent? | No | No | Yes, actor_type is "agent" |
| Same agent at another app? | No | No | Yes, the same sub |
| Who is accountable? | The user who granted it, if anyone | The person, assuming they signed in themselves | The owner, as a claim |
Notice what doesn't change across the three. None of them tells the app what the agent is allowed to do inside the product. Permissions stay with your app, which is where they belong, because only your app knows what an order or a refund is. The agent token is still a normal OpenID Connect token underneath. It is signed with ES256, its keys are published for any app to check, and the sign-in is an ordinary authorization code flow with PKCE. What makes it verifiable is the standard; what makes it useful for agents is the two extra facts it carries.
When is OAuth alone the right tool?
OAuth alone is the right tool when the agent is acting inside a person's own account, with that person's permission. An agent reading a user's calendar or filing a ticket in the user's name should hold a scoped grant from that user, and the user is rightly the one accountable, because the actions are theirs.
That model has a clear accountability story, and it keeps working as long as the agent never needs an account of its own. Revoking the grant cuts the agent off from that user's data and nothing else. It stops being enough when the agent is the one with the account: signing up for a service, holding its own history, getting its own receipts. There the app needs to know which agent it is dealing with and who stands behind it, and a grant of permission can't tell it either. The parts an agent identity needs to carry are laid out in what is an agent ID, and how an AI agent proves its identity with AgentID goes through every claim.
AgentMail gives your agents real inboxes. Create inboxes via API. Send and receive Emails with 0 complexity. Free to start.
