# auth.md — authenticating an agent with roji

> Version 1.2.0. Machine-readable metadata:
> https://api.roji.ai/.well-known/oauth-authorization-server (RFC 8414) and
> https://mcp.roji.ai/.well-known/oauth-protected-resource (RFC 9728).

roji is an agent-commerce platform: agents discover real-world services,
order them, and pay for them over MCP at https://mcp.roji.ai. Ordering spends real
money, so every agent token is bound to an accountable human or organization.
There is no anonymous agent access.

## The short version

```
claude mcp add --transport http roji https://mcp.roji.ai
```

Any conformant MCP client drives the whole flow below on its own. The rest of
this document is for clients that implement OAuth themselves.

## The discovery chain

1. **Probe the resource.** `POST https://mcp.roji.ai/` without a token returns
   `401` with:

   ```
   WWW-Authenticate: Bearer resource_metadata="https://mcp.roji.ai/.well-known/oauth-protected-resource"
   ```

2. **Read the protected-resource metadata** (RFC 9728) at that URL. It names
   the authorization server:

   ```json
   { "resource": "https://mcp.roji.ai", "authorization_servers": ["https://api.roji.ai"] }
   ```

3. **Read the authorization-server metadata** (RFC 8414) at
   `https://api.roji.ai/.well-known/oauth-authorization-server`. The AS is
   `https://api.roji.ai` — not `https://auth.roji.ai`, which serves only the
   consent screen. `https://mcp.roji.ai/.well-known/oauth-authorization-server`
   302-redirects here for clients that probe the resource origin.

## Registering a client

Two ways, no pre-shared secret in either:

- **Client ID Metadata Documents (preferred).** Use an `https:` URL as your
  `client_id`, serving your client metadata at that URL. Advertised as
  `client_id_metadata_document_supported: true`.
- **Dynamic Client Registration** (RFC 7591, anonymous).
  `POST https://api.roji.ai/oauth/register` with `{"redirect_uris": [...],
  "client_name": "..."}` returns a `client_id`.

Clients are public: `token_endpoint_auth_methods_supported: ["none"]`.
PKCE with `S256` is required, not optional.

## Getting a token

Authorization code + PKCE is the only interactive grant, and
`refresh_token` the only other one. There is **no client-credentials
grant** — an agent cannot mint itself credentials, because every order needs
a principal who answers for the spend.

1. Send the human to `https://api.roji.ai/oauth/authorize` with
   `response_type=code`, `code_challenge_method=S256`, and the RFC 8707
   `resource=https://mcp.roji.ai` parameter.
2. They sign in and approve the scopes on the consent screen at
   `https://auth.roji.ai`. That approval is the moment the agent becomes
   accountable to them.
3. Exchange the code at `https://api.roji.ai/oauth/token` for an ES256 access
   token plus a rotating refresh token. Authorization responses carry the
   RFC 9207 `iss` parameter; verify it.
4. Call `https://mcp.roji.ai` with `Authorization: Bearer <token>`. Header is the
   only accepted bearer method.
5. Revoke at `https://api.roji.ai/oauth/revoke`. A human can also revoke an
   agent's connection from their roji account at any time, and the agent can
   revoke its own with the `revoke_oauth_connection` MCP tool.

Public keys for verification: `https://api.roji.ai/.well-known/jwks.json`.

## The setup-code exchange (owner bootstrap)

There is one other way an agent obtains a credential, and it is documented
here because an agent that finds it undocumented is right to refuse it.

`POST https://api.roji.ai/agent-setup/exchange` trades a **single-use setup code**
for an ordinary account credential. It is unauthenticated because the code IS
the credential being presented.

```json
POST https://api.roji.ai/agent-setup/exchange
{"code": "stp_XXXXXXXX-XXXXXXXX-XXXXXXXX", "client": "your-client-name"}
```

Returns `{ token, tokenType, accountId, accountName, expiresAt, scopes,
apiBaseUrl, grantSummary }`. Use `token` as `Authorization: Bearer`
against `https://api.roji.ai`. **Read `grantSummary` back to the human verbatim** —
it names what was granted, and it is what replaces the consent screen this
path skips.

What the code is, exactly:

- **Minted only by an authenticated owner or admin**, server-side, when they
  load `https://roji.ai/a/{slug}/start` in a browser. There is no self-serve
  mint and no endpoint an agent can call to get one. The code carries the
  proof that a human who owns the account was present.
- **Single-use, ever.** The second presentation of a code — by anyone —
  is `410`. If you get `410` and you did not already exchange it,
  somebody else did: say so, and do not retry.
- **Short-lived**: 60 minutes from when the page issued it.
- **Not privileged.** It yields the same account-scoped credential the
  console would mint, with the same scopes and the same revocation path.
- **Not a client-credentials grant.** It does not let an agent mint itself
  anything: it only carries a human's already-proven authority across the
  gap where a browser would otherwise have to open. If you do not have a code
  a human handed you, this endpoint is closed to you.

The credential authenticates `https://api.roji.ai`, **not** `https://mcp.roji.ai`. The
MCP server accepts only OAuth-issued tokens (above). Both surfaces reach the
same commerce operations; the MCP tools are adapters over the same API.

On failure the response carries a machine-readable `code` and a
`remedy` string: `setup_code_unknown` (404),
`setup_code_spent` / `setup_code_expired` / `setup_code_revoked`
(410), `setup_code_account_inactive` (403), and
`setup_code_store_unavailable` (503 — transient, the code was NOT consumed,
present the same code again). For every 410 and the 404 the recovery is the
same and the remedy says so: ask the human to reload
`https://roji.ai/a/{slug}/start` and paste the new prompt. Do not retry a dead
code.

## Scopes

```
mcp:platform:read, mcp:platform:write, mcp:admin:read, mcp:admin:write
```

Read scopes cover catalogue search and order reads; write scopes cover
placing orders, answering questions, and requesting refunds. Spend limits are
enforced platform-side and there is no scope that lets an agent raise its own
ceiling.

## What this does not do

- **No agent self-signup.** A human creates the roji account; the agent is
  then authorized against it. There is no ceremony that mints an agent an
  account of its own.
- **No client-credentials grant**, per above. The setup-code exchange is not
  one: it spends a single-use code a human minted and handed over, and there
  is no way for an agent to obtain a code by itself.
- **No identity assertions (ID-JAG) and no revocation webhooks.** If you need
  to know a connection was revoked, the next call fails with `401`.

## Machine-readable registration (agent_auth)

The OAuth metadata document above carries an `agent_auth` block (per the
auth.md spec) so a registry or an agent can find the registration flow without
reading this page:

- `register_uri` — RFC 7591 dynamic client registration, the same endpoint
  described under "Registering a client".
- `identity_types_supported: ["anonymous"]` — registration needs no prior
  identity. This is the only type ROJI supports today: there is no
  identity-assertion or events endpoint, and none is advertised.
- `anonymous.credential_types_supported: ["access_token"]` — what an
  anonymous registration ultimately yields: an OAuth access token (plus a
  refresh token), never a long-lived secret.
- `claim_uri` — the authorization endpoint. In auth.md terms the "claim
  ceremony" is where a human takes ownership of an anonymously registered
  agent; on ROJI that is the authorization-code consent step described under
  "Getting a token". An anonymous registration holds no scopes until a human
  has completed it.
- `revocation_uri` — RFC 7009 token revocation.

If a flow is not listed there, it does not exist yet. The block and this page
are updated in the same change, so neither is ahead of the other.

## Related discovery surfaces

- Agent skill manual: https://roji.ai/skill.md
- Content catalogue: https://roji.ai/llms.txt
- MCP server card: https://mcp.roji.ai/.well-known/mcp/server-card.json
- API catalog (RFC 9727): https://api.roji.ai/.well-known/api-catalog
