# Agent authorization — agentcommunity.org

> This is an open WorkOS-authored auth.md profile composed with IETF OAuth standards; it is not an IETF RFC.
> It documents only the current `service_auth` method for read-only own-account access.

## Discover

The protected resource is `https://agentcommunity.org/api`. Discover it at
`https://agentcommunity.org/.well-known/oauth-protected-resource/api`, then discover its issuer at
`https://agentcommunity.org/.well-known/oauth-authorization-server`. Verify service-signed JWTs
with the public Ed25519 keys at `https://agentcommunity.org/.well-known/jwks.json`.

The root `/.well-known/oauth-protected-resource` describes a separate credential-free surface.
The Agent Community `/.well-known/openid-configuration` route returns JSON `404`; human browser
membership uses Supabase's canonical issuer metadata directly and is not this agent flow.

## Pick a method

Use `service_auth` when a human account owner should approve an agent for the two initial read
scopes: `agent.account.read` and `agent.registrations.read`. No provider registration, browser
OAuth-code flow, or machine-driven OTP flow is part of this profile.

## Register

Send strict JSON to `POST /agent/identity`. `client_name` is optional (one to 120
non-control characters, without leading/trailing whitespace) and `scopes` is optional. Omitting
`scopes` defaults to `agent.account.read`, then `agent.registrations.read`. When supplied,
scopes must be unique; the canonical order is `agent.account.read`, then
`agent.registrations.read`. Request only the least-privilege single scope, such as
`agent.account.read`, that the agent needs.

For both current read scopes:

```json
{
  "type": "service_auth",
  "login_hint": "user@example.com",
  "client_name": "Example read-only agent",
  "scopes": ["agent.account.read", "agent.registrations.read"]
}
```

For the least-privilege account-only case:

```json
{
  "type": "service_auth",
  "login_hint": "user@example.com",
  "client_name": "Example account reader",
  "scopes": ["agent.account.read"]
}
```

The response has the current WorkOS envelope:

```json
{
  "registration_id": "2f6d7c1e-408d-4fe1-a6c8-7adce61fd0f9",
  "registration_type": "service_auth",
  "claim_url": "https://agentcommunity.org/agent/identity/claim",
  "claim_token": "clm_...",
  "claim_token_expires": "2026-08-02T12:00:00.000Z",
  "post_claim_scopes": ["agent.account.read", "agent.registrations.read"],
  "claim": {
    "user_code": "123456",
    "expires_in": 600,
    "verification_uri": "https://agentcommunity.org/agent/authorize?claim_attempt_token=cat_...",
    "interval": 5
  }
}
```

If the ten-minute attempt expires while the outer claim remains active, rotate it by sending strict
JSON `{"claim_token":"clm_...","email":"user@example.com"}` to
`POST /agent/identity/claim`. The email must normalize to the original login hint. A successful
refresh closes the old attempt immediately and returns no outer claim token:

```json
{
  "registration_id": "2f6d7c1e-408d-4fe1-a6c8-7adce61fd0f9",
  "claim_attempt_id": "0e4ab9ab-4aaa-4d1a-9841-0bdc34fc0854",
  "status": "initiated",
  "expires_at": "2026-08-01T12:10:00.000Z",
  "claim_attempt": {
    "user_code": "654321",
    "expires_in": 600,
    "verification_uri": "https://agentcommunity.org/agent/authorize?claim_attempt_token=cat_...",
    "interval": 5
  }
}
```

## Claim

Show the human the verification URI and user code. The human approves or denies the delegation in
their browser. The agent waits at least the returned `interval` and polls with `POST /oauth2/token`
using the `urn:workos:agent-auth:grant-type:claim` grant and form data:

`grant_type=urn%3Aworkos%3Aagent-auth%3Agrant-type%3Aclaim&claim_token=clm_...`

While approval is pending, the response is:

```json
{
  "error": "authorization_pending",
  "error_description": "Authorization is still pending"
}
```

A successful poll returns a fresh access_token and a fresh identity_assertion each time:

```json
{
  "access_token": "aca_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "agent.account.read agent.registrations.read",
  "identity_assertion": "<service-signed JWT>",
  "assertion_expires": "2026-08-01T12:05:00.000Z"
}
```

### Exchange the assertion

Exchange the assertion at `POST /oauth2/token` with the
`urn:ietf:params:oauth:grant-type:jwt-bearer` grant and the exact API resource:

`grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=%3Cservice-signed-JWT%3E&resource=https%3A%2F%2Fagentcommunity.org%2Fapi`

The assertion `aud` is `https://agentcommunity.org`; the access token is for resource
`https://agentcommunity.org/api`. The same assertion can be re-exchanged while valid to obtain a
fresh access token. That response contains only `access_token`, `token_type`, `expires_in`,
and `scope`; it never returns a new assertion or refresh token. No long-lived renewal credential
is issued.

## Use

Send the access token only in the Authorization header:

`Authorization: Bearer aca_...`

- `GET /api/v1/agent/account` requires `agent.account.read` and returns only the verified
  account ID/email plus the current authorization envelope.
- `GET /api/v1/agent/registrations` requires `agent.registrations.read` and returns zero or one
  own registration with only `id`, `registration_type`, and `status`.

Neither route accepts query parameters, request bodies, cookie credentials, or a token in any
location other than `Authorization`. The same Bearer token can be reused until expiry or
revocation. It never enables broad directory search, public-profile feeds, member administration,
or a human browser session.

## Errors

- `invalid_request` — correct the strict request shape, media type, required form field, or
  duplicate field and submit a new request.
- `unsupported_grant_type` — use one of the two advertised grants: the claim grant or JWT Bearer
  grant.
- `invalid_target` — use the exact protected resource `https://agentcommunity.org/api` in a
  JWT Bearer exchange.
- `invalid_grant` — the claim or assertion is invalid, expired, revoked, or unusable; begin a
  new registration.
- `authorization_pending` — wait at least the advertised interval before polling again.
- `slow_down` — increase the current polling interval by five seconds and honor `Retry-After`.
- `access_denied` — the human denied the delegation; stop.
- `expired_token` — the current ten-minute attempt may have expired; refresh it with
  `POST /agent/identity/claim` using the existing outer claim token. If that refresh returns
  `claim_expired` or `invalid_claim_token`, begin a new registration.
- `invalid_claim_token` — the supplied claim token is invalid; begin a new registration.
- `claim_expired` — the outer claim expired; begin a new registration.
- `claimed_or_in_flight` — do not retry the same refresh; wait for the active attempt or begin a
  new registration after it closes.
- `rate_limited` — honor `Retry-After`, wait, then retry the same safe request.
- `temporarily_unavailable` — retry after a delay; do not change scopes or credentials while
  retrying.
- JSON `404` — discovery is unavailable in the current release mode or the path is wrong.

Protected child resources use a small RFC 6750 challenge surface. A missing credential returns
`Bearer realm="agentcommunity.org"`; an invalid token adds `error="invalid_token"`; both 401
forms also include the exact
`resource_metadata="https://agentcommunity.org/.well-known/oauth-protected-resource/api"`
discovery pointer. A valid token lacking the route scope returns
`Bearer realm="agentcommunity.org", error="insufficient_scope", scope="<exact route scope>"`.
The 400/403 challenges omit `resource_metadata`; all challenges omit error descriptions and error
URIs.

## Revocation

`POST /oauth2/revoke` accepts only a Bearer access token, submitted as `token=aca_...`. It
idempotently revokes that one access_token. An unknown token still returns HTTP 200; repeated calls
are idempotent.

Cancelling the whole delegation is owned by an authenticated member API/dashboard control. A
registration identifier is not accepted by `/oauth2/revoke` and must never be sent there. For
correction or data deletion, contact https://agentcommunity.org/contact.
<!-- Current registry paths: /api/v1/globe, /api/v1/content, /api/v1/content-search, /api/v1/batch, /ask, /mcp, /mcp/docs, /a2a, https://dmv.agentcommunity.org/api/register, /.well-known/agent, /.well-known/jwks.json, /.well-known/oauth-protected-resource/api, /.well-known/oauth-authorization-server, /api/v1/agent/account, /api/v1/agent/registrations, /agent/identity, /agent/identity/claim, /oauth2/token, /oauth2/revoke -->
