# Authentication — Favorited Agency Dashboard MCP

Resource: `https://mcp.agency.preprod.favorited.app/mcp` · Authorization server (issuer): `https://id.fav.com` · Version 1.0.0

This host is an OAuth 2.1 **resource server**. It issues nothing and accepts only access tokens minted by the issuer above, bound to one agency and one member. No API keys, no client secrets, no cookies, no Web Bot Auth.

## 1. Discover

1. `GET https://mcp.agency.preprod.favorited.app/.well-known/oauth-protected-resource` (also `…/oauth-protected-resource/mcp`) → `authorization_servers: ["https://id.fav.com"]`, `scopes_supported: ["mcp:read","mcp:write"]`.
2. `GET https://id.fav.com/.well-known/oauth-authorization-server` → endpoints, `code_challenge_methods_supported: ["S256"]`, `client_id_metadata_document_supported: true`, `resource_indicators_supported: true`.

## 2. Register

- **Client ID Metadata Documents (preferred):** use an `https://` URL that serves your client metadata JSON (echoing its own `client_id`, with your `redirect_uris`) as the `client_id`. Nothing to register in advance.
- **Dynamic registration (RFC 7591):** `POST https://id.fav.com/oauth/register` with `redirect_uris`, `application_type`, `token_endpoint_auth_method: "none"`. Public clients only; rate limited.
- **Pre-registered ids:** Claude, ChatGPT and Cursor are pre-registered with their published redirect URIs.

## 3. Authorize

`GET https://id.fav.com/oauth2/auth?response_type=code&client_id=…&redirect_uri=…&code_challenge=…&code_challenge_method=S256&resource=https%3A%2F%2Fmcp.agency.preprod.favorited.app%2Fmcp&scope=agency:mcp:read%20agency:mcp:write%20offline_access&state=…`

- PKCE S256 is mandatory; `resource` is mandatory and must be exactly the resource above (RFC 8707).
- The member signs in with their Favorited account, picks the agency, sees the scopes in plain words, and chooses which of their own permissions the agent may use (the consent ceiling). Write access is offered only when their role and the account allow it.
- The authorization response carries `iss` (RFC 9207). Codes are single-use and live 60 seconds.

## 4. Use

- `POST https://id.fav.com/oauth/token` with `grant_type=authorization_code`, the code, `code_verifier`, `redirect_uri` and `resource` → a 1-hour access token (`aud` = the resource) and, with `offline_access`, a refresh token.
- Send `Authorization: Bearer <access token>` to `https://mcp.agency.preprod.favorited.app/mcp`. A 401 carries `WWW-Authenticate: Bearer resource_metadata="…", scope="agency:mcp:read"`; a write on a read-only token answers 403 with `error="insufficient_scope", scope="agency:mcp:read agency:mcp:write"` — re-authorize once with both scopes.
- Refresh tokens rotate on every use (30-day sliding life); reusing a rotated token revokes the whole connection.

## 5. Revoke

- `POST https://id.fav.com/oauth/revoke` with the refresh token, or the member disconnects the app under Settings → Access → Connected apps in the dashboard.
- A role change that removes a permission narrows the connection automatically; removing the member ends it.

## Not offered

Web Bot Auth (every route is public documentation or OAuth-protected), API keys, client secrets, multi-agency tokens. Machines never reach the dashboard API directly — this server exchanges the inbound token for a short-lived API token.
