+
+
+
+
+
+
+
+
Blog/Guides

How to Let an AI Agent Sign In to Your Website

BPBinoy Perera

AI agent login for your website: add AgentID, the OpenID Connect provider built for agents, to the login you already have. Two configuration values, no SDK, and the agent signs in from a browser it enrolled once. Where sub, email, and owner_email actually arrive.

Guide
Guides
AgentID
ai-agent-login
oidc
TL;DR
  • No SDK, just a provider entry. AgentID is standard OpenID Connect. If your login accepts Google, it can accept AgentID with a configuration change.
  • The agent signs in on its own. An enrolled browser holds the agent's key and completes the flow without a human. Agents with no browser hand the sign-in to their owner.
  • You get identity in the token and accountability from /userinfo. sub and email arrive in the id_token. owner_email is served only from /userinfo, and only to registered clients.
  • Fastest path: npx @agentmail/agentid-cli init in your application's directory detects your auth provider and configures it.

To let an AI agent sign in to your website, add an OpenID Connect provider built for agents to the login you already have.

With AgentID, the sign-in for AI agents from AgentMail, that is two configuration values in your auth platform and no SDK:

  • Issuer: https://auth.agentid.com
  • Client ID: a client_id

The agent completes the sign-in from a browser it enrolled once. Your callback exchanges the code exactly as it would for Google or GitHub, and the id_token gives you a stable subject and the agent's verified email address.

If you need to know which human owns the agent, register a client, request the owner_email scope, and read it from the /userinfo endpoint with the access token.

The whole setup takes minutes on Clerk, Supabase, Auth0, Better Auth, or Auth.js, and it is free.

How does authentication for AI agents differ from a normal login?

A normal login assumes a person. An AI agent cannot complete a person's login steps as itself, so today it completes them as its owner.

Your login assumes a person in three places:

  • OTP: it sends a code to a phone or an inbox the person can read.
  • Password: it asks for a password the person remembers.
  • Consent: it shows a consent screen the person clicks.

So the agent signs up with the owner's email, reads the owner's OTP, and clicks Approve in the owner's session.

From your side, the result is indistinguishable from the owner signing up. That is the problem:

  • You cannot tell agents from people.
  • You cannot count how many agents one person is running.
  • You cannot cut off the agent without cutting off the person.

AgentMail hit this from the other direction. After it shipped an API endpoint that let agents sign up directly, it started emailing new users to ask how they had heard about the product.

Many of them did not know they had signed up at all, because their agent had done it. Most support requests began arriving from agents.

One large enterprise saw hundreds of signups from AgentMail-domain addresses in a short window. It had no way to tell whether that was one person's agent fleet or hundreds of customers.

An AI agent login fixes this by giving the agent an identity of its own that your app can verify, and a way to reach the human behind it.

What does AgentID return when an agent signs in?

When an agent signs in, AgentID returns an ES256-signed id_token carrying the agent's stable subject and verified email address. Owner claims are served separately, from /userinfo.

AgentID is an OpenID Connect provider. The agent's identity is an email address on AgentMail, so every sign-in gives you something you can already handle.

The id_token, signed with ES256, carries these claims:

ClaimWhat it isWhere it arrives
subStable subject derived from the agent's inbox. Same agent, same sub, every sign-in.id_token
email, email_verifiedThe agent's own inbox address, verified live when the token is minted. email_verified is always true when present.id_token (email scope)
nameThe agent's display name.id_token (profile scope, registered clients)
scopeWhat this sign-in granted, so you can check rather than assume.id_token (registered clients)
owner_email, owner_nameThe human who owns the organization behind the agent./userinfo only, never the id_token (owner scopes, registered clients)
iss, aud, exp, iat, jti, nonceStandard OIDC claims. jti names this one sign-in.id_token

Each claim has a job:

  • sub is what you key your agent accounts on.
  • email is how you reach the agent. Receipts, confirmations, and support replies all land in an inbox the agent reads.
  • The owner claims let you enforce a per-human limit or contact a person when something goes wrong.

How do you add auth for AI agents to your existing platform?

Add AgentID through your auth platform's custom OIDC provider setting. Every supported platform has one.

You give it the issuer, a client_id, and optionally a client_secret and the scopes to request. It does discovery from https://auth.agentid.com/.well-known/openid-configuration to find the endpoints and keys.

The per-platform guides walk each dashboard step by step:

The details that trip people up:

Supabase. The provider identifier carries a fixed custom: prefix, so you type only agentid and your code calls signInWithOAuth({ provider: 'custom:agentid' }).

Its Scopes field is comma-separated and defaults to nothing, which falls back to openid and email. Add profile there. Free projects are capped at three custom providers.

Better Auth. It uses AgentMail's helper package @agentmail/agentid-better-auth on top of the Generic OAuth plugin (Better Auth 1.7.2 or newer).

The helper fixes the provider id to agentid, so your redirect URI is your origin plus Better Auth's callback path ending in /agentid.

Auth0. Auth0 ships AgentID as a Marketplace social connection. It appears in the connections list rather than needing a hand-built enterprise connection.

Any other stack. The custom OIDC guide covers what a generic connector form will ask for, and which of its defaults will not work against this issuer:

  • No refresh token, because authorization_code is the only grant.
  • No offline_access scope.
  • No end-session endpoint.

Should you set up AgentID from the dashboard or the CLI?

For most projects, set up AgentID with the CLI. Run this in the directory of the application you want to configure:

npx @agentmail/agentid-cli init

The CLI does four things:

  1. Detects Clerk, Auth0, Supabase, Better Auth, or Auth.js v4 in the project, or takes your callback URLs directly for any other OIDC stack.
  2. Registers a client. This opens a browser for your organization to approve; there is no unattended bypass.
  3. Writes the provider configuration.
  4. Verifies the result.

At the scope picker, press Enter for standard agent identity (openid, email, profile), or pass --owner-email to also request the owner's address.

npx @agentmail/agentid-cli doctor checks an existing setup without changing anything. It exits non-zero on failure, which makes it usable in CI.

The CLI configures your application to accept agents. It does not log an agent in; that side is covered next.

How does the agent actually complete the sign-in?

The agent completes the sign-in from a browser it enrolled once. After enrollment, it signs in on its own with a fresh signature each time.

Enrollment takes three steps:

  1. The agent posts an AgentMail API key to the enrollment endpoint.
  2. JavaScript on auth.agentid.com generates a keypair inside that browser.
  3. The browser submits the public key to AgentID.

The private key is a non-extractable WebCrypto P-256 key stored in IndexedDB under auth.agentid.com. In AgentMail's words: "Your private key is generated and stored in your browser and cannot be exported through browser JavaScript APIs."

A headless browser works fine.

After that, when your app redirects the agent to /v0/authorize, the enrolled browser recognizes the session. It signs a fresh, one-time signature over a server-generated transaction.

A disclosure screen shows which app is asking and what it will receive: the agent's inbox and name, and the owner's email and name if you requested them. Sign-ins that start from the Providers page in the AgentMail console skip that screen.

No human is involved when the agent has a browser.

If the agent has no browser, its owner completes the sign-in instead:

  1. The owner starts the sign-in from your app in their own browser.
  2. They choose Approve manually.
  3. They authenticate in the AgentMail console.
  4. They pick the agent's inbox and complete the approval there.

It has to finish in that same browser; there is no code to carry to another session. Either way, your callback receives an ordinary authorization code.

What does the flow look like on the wire?

On the wire, an AgentID sign-in is the standard OpenID Connect authorization code flow: an authorization request, then a code exchange.

If you implement OIDC yourself rather than through a platform, the two requests are the standard ones. The authorization request must use PKCE with S256 for open clients. Registered clients may use PKCE or an OIDC nonce.

GET https://auth.agentid.com/v0/authorize
  ?response_type=code
  &client_id=https://yourapp.com
  &redirect_uri=https://yourapp.com/callback
  &scope=openid%20email
  &state=<opaque>
  &code_challenge=<base64url(sha256(verifier))>
  &code_challenge_method=S256

Your callback exchanges the code:

POST https://auth.agentid.com/v0/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=<code>
&redirect_uri=https://yourapp.com/callback
&client_id=https://yourapp.com
&code_verifier=<verifier>

Verify the id_token against https://auth.agentid.com/v0/jwks.json with whichever JOSE library you already use. Check iss and aud as you would for any provider.

Keys rotate with stable key ids and an overlap window, so caching by kid is safe.

How do you find out which human owns the agent?

To find out which human owns the agent, register a client, request the owner_email scope, and read it from /v0/userinfo.

Registration is one RFC 7591 call to /v0/register, authenticated with your own AgentMail API key. The same step can also be done in the AgentID console or by the CLI.

Registration gives you:

  • A client_secret, shown once.
  • Up to 10 redirect URIs.
  • Access to the owner scopes: profile, owner_profile, and owner_email, which open clients cannot request.

Then request owner_email at /v0/authorize, exchange the code as above, and call:

GET https://auth.agentid.com/v0/userinfo
Authorization: Bearer <access_token>

The response includes owner_email and, with owner_profile, owner_name.

These are held on the grant and served only here, never inside the id_token, so they cannot leak into request logs or client-side token caches.

If the agent's credential does not permit owner disclosure, the sign-in fails with a 403 rather than handing you a token with the claim missing. You can rely on the field being present whenever a sign-in succeeds.

The mechanics and the policy questions around this are in how to verify the human behind an AI agent.

What makes an AI agent login secure?

An AI agent login with AgentID is safer than the status quo, where agents sign in as people, because nothing reusable crosses the sign-in.

  • The agent's private key stays in its browser.
  • AgentID's own signing key is non-exportable.
  • Every sign-in is a fresh signature that your app verifies against published keys.

Your app never receives a password or an API key. Revoking a credential stops that one agent; it touches nothing belonging to the owner and nothing belonging to other agents.

What the token does not tell you is whether the agent will behave. It proves which agent this is, not that it is well-intentioned.

The accountability comes from the owner claim. An agent whose human you can name is an agent you can allow, limit, or ban with a real address to write to.

Most of the relying-party decisions that follow, such as how many agents one owner may run or whether to gate certain features to agents with a verified owner, are covered in can an AI agent sign up for a SaaS.

What should your app do after an agent's first sign-in?

After an agent's first sign-in, key its record on sub, store the owner if you requested it, and send your normal onboarding email.

  1. Key records on sub, not on email. The address is stable too, but sub is the identifier the protocol guarantees.
  2. Store owner_email alongside it if you requested it, and index on it so per-owner limits are one query.
  3. Send your normal onboarding email to the agent's address, because the agent will read it.
  4. Add your app to the AgentID directory so agents browsing for services that accept them can find yours.

The button guide covers the markup and the brand rules for the login page itself.

AgentID gives your agent a verified identity and its own email address. Free for apps to add.

FAQ

Let your agent sign in. Give it an AgentID and its own email address.