> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agent-loadout.com/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenID Connect for Apps: Accept Sign in with Agent Loadout

> Issuer, endpoints, client tiers, scopes, claims and token verification for apps that accept Sign in with Agent Loadout, with setup for Clerk, Supabase, Auth0, Better Auth and Auth.js.

Sign in with Agent Loadout is a standard OpenID Connect provider. If your app already accepts Sign in with Google, it accepts agents with two configuration values: the issuer and a client id.

## Issuer and discovery

```
Issuer     https://id.agent-loadout.com
Discovery  https://id.agent-loadout.com/.well-known/openid-configuration
```

| Endpoint | Path |
| - | - |
| Authorization | `/authorize` |
| Token | `/token` |
| Userinfo | `/userinfo` |
| JWKS | `/jwks.json` |
| Registration (RFC 7591) | `/register` |

The discovery document lists `authorization_code` as the only grant type, `code` as the only response type, `S256` as the only PKCE method and `ES256` as the only signing algorithm. There are no refresh tokens and no `end_session_endpoint`: the sign-in proves who the agent is, and your app's session takes over from there.

## Client tiers

<Tabs>
  <Tab title="Open client">
    No registration. Your `client_id` is an HTTPS origin you control, for example `https://yourapp.com`. The redirect URI must sit on that origin, PKCE with `S256` is mandatory, and the token request uses `token_endpoint_auth_method` `none`. Open clients may request `openid`, `email` and `profile`. Because the origin is the trust anchor, an open client cannot run on `http://localhost`; register an application for local development.
  </Tab>

  <Tab title="Registered application">
    Created under **Sign-in apps** in the dashboard, with the [Applications API](/api-reference/identity/applications), or by posting RFC 7591 client metadata to `/register` with an organization key as the Bearer token. You receive an opaque `client_id` (`alap_…`) and a client secret shown once. Registered applications get up to ten redirect URIs including loopback ones, confidential token exchange (`client_secret_basic` or `client_secret_post`), a sign-in history, the `owner_profile` and `owner_email` scopes and a cap on sign-ups per owner. They may use PKCE or an OpenID Connect `nonce`.
  </Tab>
</Tabs>

## The flow on the wire

<Steps>
  <Step title="Redirect the agent">
    ```
    GET https://id.agent-loadout.com/authorize
      ?response_type=code
      &client_id=https://yourapp.com
      &redirect_uri=https://yourapp.com/auth/callback
      &scope=openid%20email%20profile
      &state=<opaque>
      &code_challenge=<base64url(sha256(verifier))>
      &code_challenge_method=S256
    ```

    The browser lands on a waiting page where the agent approves with its own token, or a member approves in the dashboard. Requests can be approved for ten minutes.
  </Step>

  <Step title="Receive the code">
    Agent Loadout redirects to your `redirect_uri` with `code`, `state` and `iss` (RFC 9207). Codes are single-use and valid for 60 seconds.
  </Step>

  <Step title="Exchange the code">
    ```
    POST https://id.agent-loadout.com/token
    Content-Type: application/x-www-form-urlencoded

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

    Registered applications send their secret as HTTP Basic credentials or as `client_secret`. The response carries `id_token`, `access_token`, `token_type`, `expires_in` (600) and `scope`.
  </Step>

  <Step title="Verify and create the account">
    Verify the id\_token, create or look up the account by `sub`, and store the owner if you requested it.
  </Step>
</Steps>

## Scopes and claims

Scopes are space separated. A request without `scope` means `openid email`.

| Scope | Claims | Tier |
| - | - | - |
| `openid` | `sub`, plus `iss`, `aud`, `exp`, `iat`, `jti`, `auth_time`, `nonce`, `actor_type` | both |
| `email` | `email`, `email_verified` (always `true`) | both |
| `profile` | `name`, `preferred_username`, `owner_sub` | both |
| `owner_profile` | `owner_name` | registered |
| `owner_email` | `owner_email` | registered |

Registered applications also receive a `scope` claim naming what was granted. Claims that were not granted are omitted, never nulled: test for presence. A decoded id\_token for a registered application that asked for everything:

```json theme={null}
{
  "iss": "https://id.agent-loadout.com",
  "aud": "alap_0mt3k9x4b7q2w8e1r5t6y9u0i3o2",
  "sub": "lM9vT2aR7sK4qN8wE1xC6bY0uF3hJ5pD9gL2zV7oA4Q",
  "actor_type": "agent",
  "scope": "openid email profile owner_profile owner_email",
  "email": "support@acme.loadout.email",
  "email_verified": true,
  "name": "Acme Support",
  "preferred_username": "support_acme-loadout-email",
  "owner_sub": "oW7xN2pQ4mT8vL1kR6sC9dF3gH5jB0aE2uY4zI6nP8A",
  "owner_name": "Maya Chen",
  "owner_email": "maya@acme.com",
  "auth_time": 1767224990,
  "iat": 1767225000,
  "exp": 1767225600,
  "jti": "9f1c2a44-3e77-4c19-9a2e-6b0d5f8e1c33"
}
```

`sub` identifies the agent and stays the same at every app; a deleted and recreated agent gets a new one. `owner_sub` identifies the organization and is shared by all its agents, so it is the right key for per-owner limits when you do not want to hold anyone's email address. Neither can be matched to Agent Loadout ids.

## Letting agents start the sign-in

Set a **login start URL** on a registered application (OpenID Connect `initiate_login_uri`). When an agent starts a sign-in from its side, its browser arrives there with `iss=https://id.agent-loadout.com` and `login_hint=<the agent's sub>`; your handler begins your normal sign-in with Agent Loadout as the provider, and the sign-in completes without anyone acting, because the agent granted your app beforehand. Most auth libraries have a one-line way to start a provider sign-in; point the URL at a route that calls it.

List the application in the [directory](https://agent-loadout.com/id/directory) with a description and website so agents find it; the directory marks apps with a login start URL as ones agents can sign in to directly.

## Verifying the token

1. Fetch `https://id.agent-loadout.com/jwks.json` and select the key by `kid`. Keys rotate every 90 days and a retired key stays published for two days, so caching by `kid` is safe.
2. Verify the ES256 signature.
3. Check that `iss` is exactly `https://id.agent-loadout.com` and `aud` is exactly your `client_id`.
4. Check that `exp` is in the future, and that `nonce` matches if you sent one.

`GET /userinfo` with the access token as a Bearer token returns the same claims, re-read live: an archived agent, a lost inbox or a revoked approval ends there before the token expires. Keep agent sessions short and re-read `/userinfo` before the actions that matter most.

## The owner guarantee

A registered application that requested `owner_email` never receives a successful sign-in without it. When the agent's token lacks `identity:share_owner`, the agent cannot approve on its own and an owner or admin of its workspace approves in the dashboard. A workspace without a claimed owner cannot share one at all.

## Capping sign-ups per owner

Set **Maximum sign-ups per owner** on a registered application. Agent Loadout counts the agents of one organization that hold an approval for your app and refuses the next one with `error=access_denied&error_description=signup_limit_reached` on your callback, before the account exists. `0` pauses new agents while existing ones keep signing in.

## Setting up common auth platforms

The fastest way is the CLI: `npx @agent-loadout/cli app init` in your project detects the platform, registers the application, writes the client values into `.env.local` and prints the remaining steps; `app doctor` checks the result. See [CLI setup](/api-reference/identity/cli). By hand:

<Tabs>
  <Tab title="Clerk">
    Add a **Custom OpenID Connect provider** under SSO connections with issuer `https://id.agent-loadout.com`, your client id and secret, and scopes `openid email profile`. Clerk keeps standard claims on the user; fetch `owner_email` from `/userinfo` in your callback and store it yourself.
  </Tab>

  <Tab title="Supabase">
    Add a custom OIDC provider with the issuer and your client id and secret. Supabase expects scopes separated by commas: `openid,email,profile`. Allow `owner_sub`, `owner_name` and `owner_email` in the claims allowlist when you request them.
  </Tab>

  <Tab title="Auth0">
    Create a **Custom Social Connection** of type OpenID Connect with the issuer, client id and secret. Map `sub`, `email` and `owner_email` in the connection's attribute mapping.
  </Tab>

  <Tab title="Better Auth">
    Use the Generic OAuth plugin with `discoveryUrl: "https://id.agent-loadout.com/.well-known/openid-configuration"`, `pkce: true`, your client id and secret, and `scopes: ["openid", "email", "profile"]`.
  </Tab>

  <Tab title="Auth.js">
    Add a provider with `type: "oidc"`, `issuer: "https://id.agent-loadout.com"`, `clientId`, `clientSecret` and `checks: ["pkce", "state"]`. Read `owner_email` from the profile callback's token.
  </Tab>
</Tabs>

## Revocation

An agent or a member of its workspace can forget an app at any time, and members can revoke the sign-in keys that stand for the agent. Both stop new sign-ins; sessions your app already issued last as long as your app decides. Keep agent sessions short and re-read `/userinfo` before sensitive actions to notice sooner.

## Button

Show the Agent Loadout mark next to your other sign-in buttons with the text **Sign in with Agent Loadout**, in the same shape and width as your Google and GitHub buttons, and wire it to the same sign-in call. The mark in three variants, HTML and React snippets and the usage rules are at [agent-loadout.com/brand](https://agent-loadout.com/brand).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.