# auth.md

**Lobby Sites MCP — authentication for agents.** You are an agent. This service supports **agentic registration** for one API,
the Lobby Sites MCP at `https://lobbyhq.app/mcp/sites`: discover → register → authorize
(a human approves) → exchange for an access_token → call the MCP → refresh →
handle revocation.

## Step 1 — Discover

- `GET https://lobbyhq.app/.well-known/oauth-protected-resource/mcp/sites` (RFC 9728) names the
  authorization server and the one scope, `mcp:sites`.
- `GET https://lobbyhq.app/.well-known/oauth-authorization-server` (RFC 8414) names every endpoint
  below. An unauthenticated call to the MCP answers `401` with a
  `WWW-Authenticate` challenge pointing at the same document.

## Step 2 — Register (RFC 7591)

`POST https://lobbyhq.app/oauth/register` with `client_name`, `redirect_uris`,
`token_endpoint_auth_method: "none"`, `grant_types: ["authorization_code",
"refresh_token"]`, `response_types: ["code"]`. Public clients only: a
`client_secret_post` registration is refused. Identity type: **anonymous** —
registration proves nothing about who you are; authorization does.

## Step 3 — Authorize (a human decides)

Send the person to `https://lobbyhq.app/oauth/authorize` with `response_type=code`,
your `client_id`, a registered `redirect_uri`, `code_challenge` (S256 only;
`plain` is refused), `state`, and `resource=https://lobbyhq.app/mcp/sites`.
They sign in (magic link or Google) and see a consent page that lists the
communities they hold an **MCP access grant** on, granted by that community's
administrator in the Lobby console. They pick exactly one and click Allow.
Without a grant there is nothing to pick and the page says to ask the
administrator. There is no claim ceremony and no way to skip the person.

## Step 4 — Exchange the code

`POST https://lobbyhq.app/oauth/token` (`application/x-www-form-urlencoded`) with
`grant_type=authorization_code`, `code`, `redirect_uri`, `client_id`,
`code_verifier`, `resource`. You receive `access_token` (`lmo_…`, 1 hour,
Bearer), `refresh_token` (`lmr_…`), `scope: "mcp:sites"`. A replayed code
revokes every token issued from it.

## Step 5 — Use it

`Authorization: Bearer <access_token>` on `https://lobbyhq.app/mcp/sites` (Streamable
HTTP MCP). The credential reaches ONE community — the one approved — and no
tool takes a community as a parameter. Read the skills first:
`https://lobbyhq.app/.well-known/agent-skills/index.json`.

## Step 6 — Refresh

`grant_type=refresh_token` at the token endpoint. Refresh tokens rotate; the
previous pair is revoked, so keep the newest.

## Revocation

- Yours: `POST https://lobbyhq.app/oauth/revoke` (RFC 7009) with either token revokes the pair.
- Theirs: an administrator can revoke the grant at any time; your next call
  answers `401` and refresh no longer works. Re-run Step 3 once a new grant exists.

## Errors

| Where | Error | Do |
| --- | --- | --- |
| register | `invalid_client_metadata` | send `token_endpoint_auth_method: "none"` and at least one `redirect_uri` |
| authorize | error page (no redirect) | your `client_id` or `redirect_uri` is not registered — re-register |
| authorize | `access_denied` in the redirect | the person cancelled, or picked a community they hold no grant on |
| token | `invalid_grant` | code used, expired, wrong verifier or wrong client — start Step 3 again |
| token | `invalid_target` | the `resource` must be `https://lobbyhq.app/mcp/sites` on both hops |
| MCP | `401 invalid_token` | expired, revoked, or the grant was pulled — refresh, else Step 3 |

Personal tokens (`lsk_…`) exist for a person pasting a credential into a
config file; an agent does not need one — the flow above is the agent path.
