# auth.md — OpenClaw Direct

You are an agent. This document tells you how to register with OpenClaw Direct
and obtain a credential for the MCP API at https://openclaw.direct/mcp, acting
on behalf of a human who has (or will create) an OpenClaw Direct account.

The flow is: discover → register a client → get the user's authorization →
exchange the code for an access token → call the API → refresh or revoke.
Follow the steps in order; do not skip ahead.

## Step 1 — Discover

Discovery is two hops. If a `401` from https://openclaw.direct/mcp sent you
here, it carried the first URL in its `WWW-Authenticate` header:

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://openclaw.direct/.well-known/oauth-protected-resource"
```

### 1a. Protected Resource Metadata (RFC 9728)

```http
GET https://openclaw.direct/.well-known/oauth-protected-resource
```

```json
{
  "resource": "https://openclaw.direct/mcp",
  "resource_name": "OpenClaw Direct",
  "resource_logo_uri": "https://openclaw.direct/icon-512.png",
  "authorization_servers": ["https://openclaw.direct"],
  "scopes_supported": ["mcp"],
  "bearer_methods_supported": ["header"]
}
```

- `resource` — the canonical URL of the API you are trying to call.
- `resource_name` / `resource_logo_uri` — display name and logo. Surface these
  to the user when you ask for consent in Step 3.
- `authorization_servers` — where the OAuth authorization server lives. The
  `agent_auth` block is published there (see 1b).
- `scopes_supported` — the scopes this resource understands.
- `bearer_methods_supported` — `"header"` means `Authorization: Bearer …`.

### 1b. Authorization Server Metadata (RFC 8414)

```http
GET https://openclaw.direct/.well-known/oauth-authorization-server
```

```json
{
  "issuer": "https://openclaw.direct",
  "authorization_endpoint": "https://openclaw.direct/oauth/authorize",
  "token_endpoint": "https://openclaw.direct/oauth/token",
  "registration_endpoint": "https://openclaw.direct/oauth/register",
  "revocation_endpoint": "https://openclaw.direct/oauth/revoke",
  "token_endpoint_auth_methods_supported": ["none"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "response_types_supported": ["code"],
  "code_challenge_methods_supported": ["S256"],
  "scopes_supported": ["mcp"],

  "agent_auth": {
    "skill": "https://openclaw.direct/auth.md",
    "register_uri": "https://openclaw.direct/oauth/register",
    "identity_endpoint": "https://openclaw.direct/oauth/register",
    "authorization_uri": "https://openclaw.direct/oauth/authorize",
    "token_uri": "https://openclaw.direct/oauth/token",
    "revocation_uri": "https://openclaw.direct/oauth/revoke",
    "identity_types_supported": ["oauth_client"],
    "registration_methods_supported": ["oauth_dynamic_client_registration"],
    "credential_types_supported": ["bearer"],
    "scopes_supported": ["mcp"]
  }
}
```

`issuer` must equal the entry you read from `authorization_servers` in 1a;
reject the pair if it does not. `agent_auth.skill` is this document.
`agent_auth.register_uri` (aliased as `identity_endpoint` for readers that use
the reference implementation's vocabulary) is where you register in Step 2.

## Step 2 — Register a client

OpenClaw Direct accepts OAuth 2.0 Dynamic Client Registration
([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)). No credential or
pre-arranged agreement is needed to register.

```http
POST https://openclaw.direct/oauth/register
Content-Type: application/json

{
  "client_name": "Your Agent",
  "redirect_uris": ["https://your-agent.example.com/callback"]
}
```

Response (201):

```json
{
  "client_id": "…",
  "client_name": "Your Agent",
  "redirect_uris": ["https://your-agent.example.com/callback"],
  "grant_types": ["authorization_code"],
  "token_endpoint_auth_method": "none"
}
```

Clients are **public**: there is no client secret, and
`token_endpoint_auth_method` is `none`. If you omit `redirect_uris`, the client
is registered with `urn:ietf:wg:oauth:2.0:oob`. Store the `client_id`; you can
reuse it for every subsequent user.

## Step 3 — Get the user's authorization

This is the consent gate, and it requires a human in a browser. There is no
headless path. Before you hand over the URL, show the user the `resource_name`
and `resource_logo_uri` from Step 1a and the scope you are requesting.

```http
GET https://openclaw.direct/oauth/authorize
      ?response_type=code
      &client_id=<your client_id>
      &redirect_uri=<your registered redirect_uri>
      &scope=mcp
      &code_challenge=<BASE64URL(SHA256(code_verifier))>
      &code_challenge_method=S256
      &state=<opaque anti-CSRF value>
```

PKCE ([RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636)) is
**mandatory** and `S256` is the only supported method — a request without
`code_challenge` is rejected. Generate a fresh high-entropy `code_verifier` per
authorization and keep it for Step 4.

The user signs in (or signs up), approves the request, and is redirected back
to your `redirect_uri` with `code` and `state`. Verify `state` matches what you
sent.

## Step 4 — Exchange the code for a token

```http
POST https://openclaw.direct/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=<code from Step 3>
&redirect_uri=<the same redirect_uri>
&client_id=<your client_id>
&code_verifier=<the verifier whose challenge you sent in Step 3>
```

Response (200):

```json
{
  "access_token": "…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "…",
  "scope": "mcp"
}
```

## Step 5 — Call the API

Present the access token as a bearer token on the MCP endpoint:

```http
POST https://openclaw.direct/mcp
Authorization: Bearer <access_token>
Content-Type: application/json
```

The endpoint speaks JSON-RPC over streamable HTTP, MCP protocol version
`2025-03-26`. The machine-readable server card —
transport, capabilities, tool surface — is at
https://openclaw.direct/.well-known/mcp.json, and the human-readable integration
guide is at https://openclaw.direct/openclaw-mcp-integration.

## Step 6 — Refresh and revoke

Access tokens live one hour. Refresh before or after expiry:

```http
POST https://openclaw.direct/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=<refresh_token>
&client_id=<your client_id>
```

Revoke a token you no longer need
([RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009)). The call is
idempotent and answers `200` whether or not the token existed:

```http
POST https://openclaw.direct/oauth/revoke
Content-Type: application/x-www-form-urlencoded

token=<access_token or refresh_token>
&token_type_hint=access_token
```

## Alternative credential — user-issued API token

If you cannot complete a browser redirect, a signed-in user can mint a
long-lived API token at https://openclaw.direct/access_tokens and hand it to
you directly. It is presented exactly like an OAuth access token:

```http
Authorization: Bearer ocd_…
```

These tokens do not expire on a schedule; the user revokes them from the same
page. Prefer the OAuth flow when a browser is available — it is scoped,
short-lived, and revocable without the user hunting for the right row.

## Scopes

`mcp` is the only scope. It is both the default and the only optional scope, so
`scope=mcp` and omitting `scope` are equivalent.

## Errors

| Code | Where | What to do |
| --- | --- | --- |
| `invalid_client_metadata` (400) | `/oauth/register` | Your registration body was rejected. Fix it — usually a malformed `redirect_uris` entry — and retry. |
| `invalid_request` (400) | `/oauth/authorize`, `/oauth/token` | A required parameter is missing or malformed. Most often a missing `code_challenge` (PKCE is mandatory). |
| `invalid_grant` (400) | `/oauth/token` | The authorization code or refresh token is expired, already used, revoked, or the `code_verifier` does not match. Restart at Step 3. |
| `invalid_client` (401) | `/oauth/token` | `client_id` is not recognised. Re-register at Step 2. |
| `unsupported_grant_type` (400) | `/oauth/token` | Only `authorization_code` and `refresh_token` are accepted. |
| `401` | `/mcp` | The token is missing, expired, or revoked. Refresh (Step 6); if that fails, restart at Step 3. |

Retry policy: on `5xx`, back off exponentially and retry the same request. On
`4xx`, do not retry the same payload — act on the table above.

## Not supported — do not probe

OpenClaw Direct does **not** implement the agentic registration ceremonies from
the wider auth.md ecosystem. The following endpoints and identity types do not
exist here, and probing them only produces `404`s:

- `POST /agent/identity` and `POST /agent/identity/claim`
- ID-JAG `identity_assertion` (`urn:ietf:params:oauth:token-type:id-jag`)
- `service_auth` (verified-email) registration
- `anonymous` registration and the `user_code` claim ceremony

Registration here means RFC 7591 dynamic client registration followed by the
user-consented authorization code flow described above. Use the discovery
documents in Step 1 as the authoritative source of what is offered.
