Blog / Guides

How to Let an AI Agent Sign In to Your Website

Binoy 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.

  • 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 and accountability in the token. sub and email arrive in the id_token. Registered apps that request the owner_email scope receive it in the id_token and from /v0/userinfo; open clients never receive it.
  • 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, create an AgentID application, request the owner_email scope, and read it from the id_token or the /userinfo endpoint.

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. Registered apps that request the owner scopes also receive owner_email and owner_name, in the id_token and from /v0/userinfo; open clients never receive them.

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.id_token and /userinfo (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:

Bash

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 Apps 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.

Code

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:

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, create an AgentID application, request the owner_email scope, and read it from the id_token or /v0/userinfo.

Click Create application in the AgentID console, or run npx @agentmail/agentid-cli init. That gives you:

  • A client_secret, shown once (you can regenerate it).
  • Up to 10 redirect URIs.
  • Access to the owner scopes: owner_profile and owner_email, which open clients cannot request.

Then request owner_email at /v0/authorize and exchange the code as above. The id_token carries owner_email, and /userinfo returns the same value:

Code

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

The response includes owner_email and, with owner_profile, owner_name.

The owner's email is personal data, so don't log tokens that carry it.

If the agent is not allowed to share its owner, it can't finish the sign-in alone; the organization owner has to approve it. You never get a token with the claim missing, so you can rely on the field 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.

FAQ

Questions, answered.

For agents acting on behalf of your own users, Auth0 for AI Agents, Descope, and Stytch add agent features to the login you run. For agents arriving from outside your organization as accounts of their own, AgentID is an OpenID Connect provider that plugs into Clerk, Supabase, Auth0, Better Auth, and Auth.js. The two kinds compose.

A fresh asymmetric signature per sign-in, verified against published keys, so no password or reusable secret ever crosses the wire. AgentID does this with a non-extractable P-256 key held in the agent's enrolled browser and ES256-signed id_tokens your app checks against the issuer's JWKS.

A stable sub derived from the agent's inbox, the agent's verified email, email_verified, and the standard iss, aud, exp, iat, jti, and nonce claims. With profile it adds name, preferred_username and owner_sub. Registered apps also get scope, and with the owner scopes, the owner's owner_name and owner_email. /userinfo returns the same identity and owner claims.

Validate the id_token's ES256 signature against https://auth.agentid.com/v0/jwks.json, check iss and aud, and key the account on sub. To verify the human behind it, create an AgentID application, request the owner_email scope, and read it from the id_token or /v0/userinfo.

Authentication is knowing which agent signed in; AgentID answers it with an OpenID Connect id_token whose subject is the agent. Authorization is deciding what that agent may do in your app, which stays with your own permission system. AgentID does not issue permissions; it gives you an identity and an owner to attach them to.

No, if the agent has an enrolled browser. It signs in on its own and sees a disclosure screen showing what the app will receive. If the agent has no browser, its owner completes the sign-in in the AgentMail console, in the same browser the flow started in.

Blog

Suggested reading

View all posts

Guides · September 15, 2026

OIDC for AI Agents: The Complete Guide

OAuth and OpenID Connect for AI agents, with the agent as the subject. The live AgentID discovery document, the code flow with PKCE S256, ES256 tokens, the two client tiers, every scope and claim, where owner_email lives, browser enrollment, and the threat model.

Guides · September 15, 2026

Adding a Sign in with AgentID Button

How to add a Sign in with AgentID button to your website: two configuration values and one image. Provider setup for Clerk, Supabase, Auth0, Better Auth, and Auth.js, the CLI shortcut, the official mark in four variants, button markup, and the button guidelines.

Prompt

Give yourself access to AgentMail
by following the guide at:

https://docs.agentmail.to/agent-onboarding

and refer to:

https://agentmail.to/llms.txt

for the documentation index.

Give your agent an identity of its own.

Create an AgentMail inbox and let your agent start signing in as a real user.