Skip to content
Docs

Add AgentID to Auth0

A custom social connection, included on every Auth0 plan.

On this page

01Register a client

Auth0’s connection form will ask for a client id and secret, so start here. Register a client in the AgentID console with your app name and redirect URI, then copy the generated client id and secret before you close the dialog: the secret is shown once. Scope selection happens in Auth0 in step 3.

The redirect URI is your Auth0 domain, from Settings → Custom Domains. Without a custom domain it is https://your-tenant.your-region.auth0.com/login/callback; with one, https://your-custom-domain/login/callback.

The console applies Auth0’s required client_secret_post authentication method automatically when you use this guide.

02Create the connection

Authentication → Social + Create Connection, then Create Custom at the bottom of the gallery.

Auth0's Social Connections page with a single existing connection, google-oauth2, and a Create Connection button in the top right. The sidebar shows Authentication expanded over Database, Social, Enterprise, Passwordless and Authentication Profile.

03Fill the form

Pick Authentication.

The New Custom Social Connection page, reached from Choose Social Connection, showing the Purpose step with three radio options: Authentication, selected; Connected Accounts for Token Vault; and Authentication and Connected Accounts for Token Vault.
The other two are Token Vault, not sign-in.
new custom social connection
Purpose                        Authentication
Name                           agentid
Authorization URL              https://auth.agentid.com/v0/authorize
Token URL                      https://auth.agentid.com/v0/token
Scope                          openid email profile
Separate scopes using a space  on
Client ID                      <client_id from step 1>
Client Secret                  <client_secret from step 1>
The General section of the form, with a value in the Name field, Authorization URL and Token URL pointing at the AgentID issuer's v0 paths, a Scope field reading openid email profile owner_email owner_profile, and the Separate scopes using a space toggle switched on.
Name cannot be changed later, and step 5 looks the connection up by it. This shot opts into owner scopes and predates the shared connector id: type agentid, lower case.

Watch out. This is the only scope choice you need to make. Add owner_email and owner_profile only if your app needs the human owner’s identity. The screenshot shows that optional pair. When requested, the agent’s signing credential must still permit those claims.

04Replace the profile script

Fetch User Profile Script ships returning {}, leaving Auth0 no subject to key a user on. This connection never reads the id_token, so /v0/userinfo is the only source of claims. Paste this, then create.

fetch user profile script
function(accessToken, ctx, cb) {
  request.get(
    {
      url: 'https://auth.agentid.com/v0/userinfo',
      headers: { Authorization: 'Bearer ' + accessToken }
    },
    function(err, resp, body) {
      if (err) return cb(err);
      if (resp.statusCode !== 200) return cb(new Error(body));
      var u = JSON.parse(body);
      cb(null, {
        user_id: u.sub,
        email: u.email,
        email_verified: u.email_verified,
        name: u.name,
        org: u.org,
        owner_name: u.owner_name,
        owner_email: u.owner_email
      });
    }
  );
}

05Turn on PKCE and name the button

This step is required. Auth0’s OAuth2 strategy does not validate an OIDC nonce, so PKCE is what binds the authorization code to the browser session. The same call sets the button’s name and mark. Token from Applications → APIs → Auth0 Management API → API Explorer.

PATCH /api/v2/connections/{id}
export AUTH0_DOMAIN=your-tenant.us.auth0.com
export AUTH0_TOKEN=<management api token>

CONNECTION_ID=$(curl -s -G "https://$AUTH0_DOMAIN/api/v2/connections" \
  -H "Authorization: Bearer $AUTH0_TOKEN" \
  --data-urlencode 'name=agentid' | jq -r '.[0].id // empty')
echo "connection: ${CONNECTION_ID:-NOT FOUND, check the Name from step 3}"

curl -s "https://$AUTH0_DOMAIN/api/v2/connections/$CONNECTION_ID" \
  -H "Authorization: Bearer $AUTH0_TOKEN" \
  | jq '{
      display_name: "AgentID",
      options: (.options + {
        pkce_enabled: true,
        icon_url: "https://auth.agentid.com/brand/icon-black.svg"
      })
    }' \
  | curl -s -X PATCH "https://$AUTH0_DOMAIN/api/v2/connections/$CONNECTION_ID" \
      -H "Authorization: Bearer $AUTH0_TOKEN" \
      -H 'Content-Type: application/json' \
      --data-binary @- \
  | jq '{display_name, pkce: .options.pkce_enabled, icon: .options.icon_url}'

A run that worked ends in "pkce": true and the two button fields.

Watch out. It reads before writing on purpose: PATCH replaces options wholesale, so a body with only pkce_enabled drops your credentials and scope. Dark tenant: icon-white.svg.

06Enable it and sign in

Creating it lands you on the Applications view. Switch it on for your app. Continue with AgentID then appears on Universal Login. The agent approves out of band, so the browser waits: approving a sign-in.

Watch out. org, owner_name and owner_email reach the Auth0 user record and stop there: Auth0 silently drops unnamespaced custom claims from the token it mints for your app. An Action carries them through. sub, email, email_verified and name arrive untouched.

The enterprise alternative. Authentication → Enterprise → OpenID Connect is the better protocol fit: discovery, id_token verification, PKCE and key rotation all handled. Auth0 meters those connections, and they put the owner claims out of reach.

Endpoints, scopes, token claims and client tiers are on the integration reference.