iss: https://id.agentboxd.com

Live

Let AI agents sign in to your app.

Agent Login is Sign in with Agentboxd: an OpenID Connect provider where the user is an AI agent. Agents prove who they are with a short-lived token instead of a password, and your app knows it is talking to an agent.

exp − iat ≤ 300
Every token lives 5 minutes at most, works once, and is addressed to one app.
sub (pairwise)
Each app sees a different, stable subject for the same agent.
OIDC · PKCE · JWKS
Standard OpenID Connect at id.agentboxd.com. Better Auth, Auth.js or any OIDC client.

https://agentboxd.com/claims/agent: true

Two sides of one sign-in.

Apps decide to accept agents; agents need a way in that isn’t a borrowed password. Agent Login is built for both.

You build an app or an API

AI agents already use your product: they sign up with passwords and click verification emails, or share a person’s credentials. Give them a front door of their own.

  • Know it is an agent. Every token carries agent: true, so you can route agents to API-first onboarding or their own limits.
  • No passwords to store and no email loop to automate. Verify a signed token and start your session.
  • Standard OpenID Connect. Discovery, JWKS, code flow with PKCE, or a server-side token exchange. No proprietary SDK required.

You build AI agents

Your agent already has an Agentboxd inbox. That inbox is also its identity: one API call and it signs in to any app that accepts Sign in with Agentboxd.

  • One call, no secrets to keep. mr.identity.token() returns a 5-minute token for one app.
  • Private by default. Each app sees a different subject, so apps can’t follow your agent across services.
  • You stay in charge. Switch sign-in off per inbox and see every app it signed in to.

POST /v1/inboxes/:id/identity-token

How it works.

Three steps, and no password anywhere. This is the headless flow, for agents that call your API or app directly.

  1. /app/identity

    Register your app

    In the dashboard, register the app and pick its type: a server app (with the button), a public app, or verify-only for agents that call your API. You get a client_id, the audience of every token.

  2. POST /v1/inboxes/:id/identity-token

    The agent asks for a token

    With one call the agent gets an ES256-signed ID token addressed to your client_id, valid for 5 minutes, and sends it to you. From an MCP client it is the get_identity_token tool.

  3. verifyAgentIdentityToken

    You verify it and start a session

    Check the signature against the public keys, the issuer, the audience, the expiry and the nonce, and reject a token seen before. The SDK does all of it; or exchange the token at the issuer, which enforces single use for you.

agent.ts · the agent’s side
import { Agentboxd } from 'agentboxd';

const mr = new Agentboxd(); // a key with identity:sign (preset "sign_in")

// A 5-minute, single-use ID token addressed to one app (its client_id).
const { id_token } = await mr.identity.token({
  inboxId: inbox.id,
  audience: 'abxc_4kQ9...',
  nonce, // optional: the one the app gave you
});

await fetch('https://app.example.com/login/agent', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ id_token }),
});
login.ts · your app’s side
import { MemoryReplayCache, verifyAgentIdentityToken } from 'agentboxd/identity'; // npm install agentboxd jose

const replayCache = new MemoryReplayCache(); // several processes: back it with Redis (SET jti 1 NX EXAT exp)

export async function signInAgent(idToken: string, nonce?: string) {
  const agent = await verifyAgentIdentityToken(idToken, {
    audience: process.env.AGENTBOXD_CLIENT_ID!, // your client_id
    nonce,        // if you handed the agent one
    replayCache,  // single use: a second presentation of the same jti throws
  });
  // agent.sub is stable for your app and different at every other app: key the user on it.
  return { sub: agent.sub, email: agent.email, isAgent: agent.isAgent };
}
id_token · decoded payload
{
  "iss": "https://id.agentboxd.com",
  "sub": "Qm9vZ2xlLXBhaXJ3aXNlLXN1YmplY3QtZXhhbXBsZQ",
  "aud": "abxc_4kQ9...",
  "iat": 1790327643,
  "exp": 1790327943,
  "auth_time": 1790327643,
  "jti": "3f2e1d0c-9b8a-4765-8432-10fedcba9876",
  "nonce": "n-3f9a2c",
  "email": "support-bot@agents.agentboxd.com",
  "email_verified": true,
  "https://agentboxd.com/claims/agent": true
}

alg: ES256

Built so a token can’t be reused.

An agent’s token is worth little to anyone who steals it: it is short-lived, single-use, and good for one app only. The details are in the docs.

  • ES256

    Signed, with rotating keys

    Tokens are signed with ECDSA P-256 keys that rotate. Verifiers fetch them from the JWKS; none and HMAC algorithms are refused.

  • ≤ 300 S

    Short-lived, no refresh tokens

    An ID token lives 5 minutes at most. There are no refresh tokens: an agent signs in again with one API call, and your session decides how long it stays.

  • SINGLE USE

    Every token works once

    Each token has a unique jti. Keep a replay cache, or exchange it at the issuer with the JWT bearer grant (RFC 7523), which records it and refuses a second use.

  • AUD = CLIENT_ID

    Bound to one app

    A token names exactly one audience, your client_id. A token issued for another app fails verification, and can’t be redeemed at the issuer by another client.

  • PAIRWISE SUB

    A different subject at every app

    Each app sees its own stable sub for the same agent, so two apps can’t join their user tables to track it. Key your users on sub.

  • AGENT: TRUE

    Agents say they are agents

    Every token carries https://agentboxd.com/claims/agent: true. The subject is an AI agent’s inbox, never a person pretending otherwise.

  • PKCE S256

    A safe browser flow

    The button uses the authorization code flow with S256 PKCE, state and nonce all required, exact redirect URIs, 60-second single-use codes and iss in the response (RFC 9207).

  • SWITCH · LOG

    Owners see and stop every sign-in

    Each inbox has a sign-in switch that takes effect at once, a history of every token and sign-in (kept 180 days), and a webhook for each one.

GET {issuer}/authorize

Add a Sign in with Agentboxd button.

For a web app, a person clicks the button: the agent’s owner. They approve on agentboxd.com and choose which of their agents’ inboxes signs in, and your app gets a normal OpenID Connect result, with agent: true in it.

Point the button at your auth library’s sign-in route for the agentboxd provider. The markup on the right is a starting point; keep the label “Sign in with Agentboxd”.

sign-in-button.html
<a class="siwa" href="/api/auth/signin/agentboxd">
  <svg width="20" height="20" viewBox="0 0 32 32" aria-hidden="true">
    <circle cx="12.5" cy="16" r="8" fill="none" stroke="#D4FF3A" stroke-width="3" />
    <path d="M22.5 11.5h7M23.5 16h6M22.5 20.5h7" stroke="#EDEAE2" stroke-width="2.2" />
  </svg>
  Sign in with Agentboxd
</a>

<style>
  .siwa { display: inline-flex; align-items: center; gap: 10px; min-height: 44px;
          padding: 0 18px 0 12px; border: 1px solid #2A2D33; border-radius: 2px;
          background: #0A0B0D; color: #EDEAE2; font: 600 15px/1 system-ui, sans-serif;
          text-decoration: none; }
  .siwa:focus-visible { outline: 2px solid #D4FF3A; outline-offset: 2px; }
</style>

/.well-known/openid-configuration

Works with the auth you already have.

Agent Login is plain OpenID Connect, so there is nothing new to learn. Point your library at the discovery document and it finds the keys and endpoints.

  • Better Auth: the Generic OAuth plugin, with PKCE and ID-token verification.
  • Auth.js (NextAuth): a small OIDC provider with pkce, state and nonce checks.
  • Any OIDC client with the authorization code flow and PKCE, in any language.
  • Headless: verifyAgentIdentityToken from agentboxd/identity, or the RFC 7523 token exchange.

The Better Auth, Auth.js and headless code is typechecked in examples/identity and walked through in the guide.

auth.ts
import { betterAuth } from 'better-auth';
import { genericOAuth } from 'better-auth/plugins';

const ISSUER = process.env.AGENTBOXD_ISSUER ?? 'https://id.agentboxd.com';

export const auth = betterAuth({
  // database: your adapter
  user: {
    additionalFields: { isAgent: { type: 'boolean', required: false, defaultValue: false, input: false } },
  },
  plugins: [
    genericOAuth({
      config: [
        {
          providerId: 'agentboxd',
          name: 'Agentboxd',
          discoveryUrl: `${ISSUER}/.well-known/openid-configuration`,
          clientId: process.env.AGENTBOXD_CLIENT_ID!,
          clientSecret: process.env.AGENTBOXD_CLIENT_SECRET!,
          scopes: ['openid', 'email', 'profile'],
          pkce: true,
          requireIdTokenVerification: true,
          mapProfileToUser: (profile) => ({
            name: typeof profile.name === 'string' ? profile.name : undefined,
            email: typeof profile.email === 'string' ? profile.email : null,
            emailVerified: profile.email_verified === true,
            isAgent: profile['https://agentboxd.com/claims/agent'] === true,
          }),
        },
      ],
    }),
  ],
});

// In the browser:
// await authClient.signIn.social({ provider: 'agentboxd', callbackURL: '/dashboard' });

Questions

Questions.

What is Agent Login?

Agent Login, or Sign in with Agentboxd, is an OpenID Connect identity provider at id.agentboxd.com where the user is an AI agent. An agent’s identity is its Agentboxd inbox, and it signs in to apps with a short-lived, single-use ID token instead of a password.

Do I need an Agentboxd account to accept agents in my app?

You need a free account to register your app and get a client_id. After that, verifying a token needs only the public keys: any OpenID Connect or JOSE library works, and the agentboxd TypeScript package has a helper that does every check. Create an account.

Which libraries does it work with?

Any OpenID Connect client that supports the authorization code flow with PKCE. The guide has working setups for Better Auth (Generic OAuth plugin) and Auth.js, and a headless relying party in TypeScript. Other clients only need the discovery URL, your client_id and, for server apps, the client secret. The guide.

How is a token kept from being replayed or stolen?

It lives at most 5 minutes, names exactly one app as its audience, carries a unique jti that your app or the issuer accepts once, and can be bound to a login attempt with a nonce. The browser flow adds PKCE, state and exact redirect URIs.

Can apps track my agent across services?

Not through Agent Login. By default each app receives a different subject for the same agent. The email claim is the same everywhere, so an app that asks for it can match on it; apps that care about privacy shouldn’t ask for it.

Who approves a browser sign-in?

The agent’s owner. The button sends them to agentboxd.com, where they see your app’s name, its redirect host and the scopes, pick which inbox signs in and approve. There is no remembered consent: every browser sign-in is approved.

What does it cost?

Agent Login is included on every Agentboxd plan, Free included. Apps that only accept tokens don’t pay anything to verify them. Pricing.

/app/identity

Open your app to agents.

Register your app in the dashboard to get a client_id, then follow the guide. Free on every plan.