# auth.md

This file is for agents that need credentials to call Done Bear. The resource server is `https://api.donebear.com`. The authorization server is `https://mcp.donebear.com`. Protected Resource Metadata (PRM) is authoritative if anything here conflicts with it.

Done Bear does not implement WorkOS `agent_auth`. There is no `register_uri` for agent identity, no `identity_assertion`, and no `id-jag` exchange. Use OAuth authorization code with PKCE, a CLI-created API key, or a user JWT.

## 1. Discover

On a 401 from `https://mcp.donebear.com/mcp`, read the `WWW-Authenticate` header:

```http
WWW-Authenticate: Bearer realm="mcp", resource_metadata="https://mcp.donebear.com/.well-known/oauth-protected-resource/mcp"
```

If the header is missing, fall back to the conventional paths:

- Site PRM: `https://donebear.com/.well-known/oauth-protected-resource`
- MCP PRM: `https://mcp.donebear.com/.well-known/oauth-protected-resource/mcp`
- Authorization server metadata: `https://mcp.donebear.com/.well-known/oauth-authorization-server`

From the PRM, read `resource`, `resource_name`, `authorization_servers`, `scopes_supported`, and `bearer_methods_supported`.

From the authorization server metadata, read `issuer`, `authorization_endpoint`, `token_endpoint`, `registration_endpoint`, `grant_types_supported`, and `code_challenge_methods_supported`. There is no `revocation_endpoint` and no `agent_auth` block (`identity_endpoint`, `claim_endpoint`, `events_endpoint`, `identity_types_supported`, `identity_assertion.assertion_types_supported`).

Supported scopes:

- `read` — list and show tasks, projects, labels, teams, workspace metadata
- `write` — create and edit those records (`read` is included)
- `admin` — workspaces, billing, members, and API keys (API keys only; MCP OAuth advertises `read` and `write`)

## 2. Pick a method

- MCP client or other OAuth public client → authorization code + PKCE S256 against `https://mcp.donebear.com`.
- Script, CI, or long-lived integration → `db_` API key from the CLI.
- Signed-in user session already in hand → Supabase JWT as `Authorization: Bearer <jwt>`.
- WorkOS `agent_auth` session, email-only `service_auth`, or `anonymous` claim → not offered. Do not POST an `identity_assertion`.

## 3. Register

OAuth clients that are not already configured register at the authorization server (RFC 7591), not at a WorkOS `register_uri`:

```http
POST /register HTTP/1.1
Host: mcp.donebear.com
Content-Type: application/json
```

```json
{
  "client_name": "Example agent",
  "redirect_uris": ["https://localhost/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}
```

The response is a standard dynamic-client-registration envelope (`client_id`, optional `client_secret`). It is not a service-signed `identity_assertion`.

API keys skip this step. A human or the CLI creates the key:

```bash
donebear api-key create "Agent" --permissions read-only
```

`read-only` issues `read`. `full-access` issues `read`, `write`, and `admin`.

## 4. Claim

There is no claim ceremony. Done Bear does not mint a `user_code`, `verification_uri`, or `claim_token`. The user signs in through the OAuth authorization-code redirect at `https://mcp.donebear.com/authorize`, or they paste a CLI-created API key.

## 5. Exchange the assertion

There is no JWT-bearer `identity_assertion` exchange and no `id-jag`. After the user approves, the client POSTs the authorization code to the token endpoint:

```http
POST /token HTTP/1.1
Host: mcp.donebear.com
Content-Type: application/x-www-form-urlencoded
```

```
grant_type=authorization_code&code=<code>&redirect_uri=<redirect_uri>&client_id=<client_id>&code_verifier=<pkce_verifier>
```

The response is a standard OAuth token envelope (`access_token`, `token_type`, `expires_in`, `scope`, and a `refresh_token` when issued).

## 6. Use the credential

Send the access token, API key, or JWT on every call:

```http
GET /mcp HTTP/1.1
Host: mcp.donebear.com
Authorization: Bearer <access_token>
```

```http
POST /graphql HTTP/1.1
Host: api.donebear.com
Authorization: Bearer <db_key_or_jwt>
Content-Type: application/json
```

When the access token expires, use the `refresh_token` if one was issued. When that fails with `invalid_grant`, restart at authorization. API keys do not expire on a timer; revoke them from the CLI when the job is done.

## 7. Errors

| Status | Body | Meaning | Action |
| --- | --- | --- | --- |
| 401 | `{"error":"Authorization header required"}` | No bearer header | Add `Authorization: Bearer <token>` |
| 401 | `{"error":"Invalid authorization format"}` | Header is not `Bearer ...` | Fix the header |
| 401 | `{"error":"Invalid API key"}` | `db_` key is wrong or revoked | Create a new key |
| 401 | `{"error":"Invalid token"}` | JWT is invalid | Refresh or sign in again |
| 401 | `WWW-Authenticate` with `resource_metadata` | MCP OAuth required | Start Discover |
| 400 | `invalid_grant` | Code, verifier, or refresh token rejected | Restart at Register / authorize |
| 400 | `unsupported_grant_type` | Grant is not authorization_code or refresh_token | Do not send jwt-bearer or claim grants |

WorkOS `agent_auth` codes (`invalid_issuer`, `interaction_required`, `authorization_pending`, `*_not_enabled`) are not returned. There is no `/agent/identity` route.

## 8. Revocation

There is no RFC 7009 `revocation_endpoint` and no RFC 8935 SET `events_endpoint`.

- OAuth access tokens die when they expire or when refresh fails with `invalid_grant`.
- API keys are revoked by the workspace owner: `donebear api-key revoke <id>` (see `donebear api-key --help`).
- A human can also revoke keys from the Done Bear app.

Do not POST tokens to a revoke URL that is not in the authorization server metadata.
