# auth.md

You are an agent. Bucky supports agentic registration: discover, register, claim if needed, exchange for an access_token, then call the API.

Bucky hosts the authorization server at the site origin. After a human claims the registration, the access_token is a Supabase user JWT. Pre-claim tokens only unlock public MCP tools.

## Step 1 — Discover

On `401 Unauthorized`, read `WWW-Authenticate` for `resource_metadata="…"`. If you do not have a 401, fetch:

```http
GET /.well-known/oauth-protected-resource
```

Then fetch Authorization Server metadata:

```http
GET <authorization_servers[0]>/.well-known/oauth-authorization-server
```

Read the `agent_auth` block. `register_uri` / `identity_endpoint` is `POST /api/auth/agent/identity`. `claim_uri` / `claim_endpoint` is `POST /api/auth/agent/identity/claim`. The token endpoint is `POST /api/auth/mcp/token`.

## Step 2 — Pick a method

1. You have the user's email → `service_auth`.
2. You have no user identity yet → `anonymous`.

This service does not accept ID-JAG (`urn:ietf:params:oauth:token-type:id-jag`). Do not send `type: identity_assertion`.

## Step 3 — Register

```http
POST /api/auth/agent/identity
Content-Type: application/json
```

Anonymous:

```json
{ "type": "anonymous" }
```

The response includes `identity_assertion`, `claim_token`, `pre_claim_scopes` (`mcp.public`), and `post_claim_scopes` (`mcp`). Exchange the assertion (Step 5) to call public MCP tools. To let a human take ownership, continue at Step 4.

Verified email:

```json
{ "type": "service_auth", "login_hint": "user@example.com" }
```

The response includes `claim_token` and a `claim` block (`user_code`, `verification_uri`, `interval`). Surface those to the user. Poll the token endpoint (Step 4c). Bucky does not create a user from `login_hint`. The human must sign in or sign up on Bucky.

## Step 4 — Claim ceremony

### 4a. Start the ceremony (anonymous)

```http
POST /api/auth/agent/identity/claim
Content-Type: application/json

{ "claim_token": "clm_...", "email": "user@example.com" }
```

`service_auth` already returned the `claim` block in Step 3.

### 4b. Hand off to the user

Tell the user: open `verification_uri`, sign in or sign up, then enter the 6-digit `user_code` on the Bucky claim page. The code goes into Bucky, not back to you.

### 4c. Poll for completion

```http
POST /api/auth/mcp/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:workos:agent-auth:grant-type:claim
&claim_token=<clm_...>
```

Wait: `{ "error": "authorization_pending" }`. Success returns `access_token` (Supabase user JWT) plus `identity_assertion` (v2). Honor `interval`. On `expired_token`, call `/api/auth/agent/identity/claim` again with the same `claim_token`. On `410 claim_expired`, restart at Step 3.

Completing the ceremony revokes any pre-claim access_token.

## Step 5 — Exchange the assertion

```http
POST /api/auth/mcp/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion=<identity_assertion>
```

Unclaimed anonymous assertions mint an opaque `mcp.public` token. Claimed assertions mint a Supabase user JWT with scope `mcp`.

## Step 6 — Use the access_token

```http
Authorization: Bearer <access_token>
```

- Pre-claim: public MCP tools at `/api/mcp` only. `/api/v1` returns 401.
- After claim: MCP gated tools and `/api/v1` accept the Supabase JWT.

There is no refresh_token in this flow. Re-run Step 5 with the current `identity_assertion`. If that returns `invalid_grant`, restart at Step 3.

## Revocation

`POST /api/auth/agent/revoke` with `token=` revokes Bucky-issued opaque pre-claim tokens and identity assertions (so jwt-bearer stops). Post-claim Supabase JWTs expire on their own; this endpoint does not revoke them.

## Related

- PRM: [/.well-known/oauth-protected-resource](/.well-known/oauth-protected-resource)
- Authorization server: [/.well-known/oauth-authorization-server](/.well-known/oauth-authorization-server)
- Agent entrypoint: [/AGENTS.md](/AGENTS.md)
- API catalog: [/.well-known/api-catalog](/.well-known/api-catalog)
