# Autenticação da API e do MCP do CFO

A API REST usa **chave de API**. O servidor MCP aceita a mesma chave ou um
**token OAuth** autorizado pela pessoa que usa a conta. Envie a credencial em
`Authorization: Bearer <CREDENCIAL>`; nunca na URL.

## Chave de API

1. Crie sua conta em <https://cfo.com.br/cadastro>.
2. Entre em <https://cfo.com.br/painel> e crie uma chave na seção de API.
3. Copie a chave: ela aparece uma única vez e começa com `cfo_live_`.

O plano Free dá acesso à API REST. O MCP exige plano pago. As cotas de consultas
são compartilhadas com a API e o site; conectar um aplicativo não cria outra cota.

```http
Authorization: Bearer <SUA_CHAVE>
```

A API também aceita `X-Api-Key`. Não envie chaves por `?api_key=` ou `?key=`:
URLs podem parar no histórico e nos logs de proxies. O recurso autenticado
`GET https://cfo.com.br/api/v1/status` informa o plano e as cotas.
Revogar uma chave no painel corta seu acesso na chamada seguinte.

## MCP: conectar sem copiar uma chave

No aplicativo que suporte MCP remoto por Streamable HTTP e OAuth, adicione:

```text
https://mcp.cfo.com.br/v1
```

O aplicativo descobre a autenticação, abre o CFO para login e mostra o pedido
de consentimento. A tela destaca quem publicou o aplicativo: o site de onde o
CFO leu a identificação dele (clientes CIMD) ou o aviso de que ele foi
registrado anonimamente. O nome é informado pelo próprio aplicativo e aparece
em segundo plano. Confira o publicador, o endereço de retorno e a conta antes
de permitir. Você pode negar o pedido ou desconectar depois em
<https://cfo.com.br/painel#conta>. Se o endereço de retorno for desconhecido e
você negar antes de confirmá-lo, o CFO mostra a recusa na própria página e não
envia resposta a esse endereço.

As ferramentas de consultas são `buscar_empresas`, `buscar_empresas_por_socio`,
`buscar_empresas_por_dominio`, `buscar_empresas_por_marca`, `dossie_empresa` e
`triagem_kyc`. As buscas por vínculo retornam empresas e aceitam filtros e paginação;
por padrão, usam `dias=0` e `situacao="2"` (ativas). Vínculos podem incluir homônimos
e a seleção é limitada a 2.000 raízes de CNPJ; resultado vazio não comprova ausência
de vínculo. `whoami` mostra a identidade e as permissões da conexão; o recurso
`cfo://guide` orienta o uso. As respostas MCP trazem o mesmo dado da API REST para
a mesma conta e plano, com as mesmas regras: nas buscas, telefone e e-mail saem
mascarados por padrão, e `revelar_contatos=true` (o `reveal=1` da API REST) revela
consumindo leads do plano. O CPF de sócio já vem mascarado da Receita Federal.

## OAuth para quem implementa um cliente

O emissor é sempre `https://cfo.com.br`. O fluxo suportado é Authorization Code
com **PKCE S256**, seguido de renovação por refresh token. O escopo é `mcp` e
permite somente consultas. Não há implicit, password ou client credentials.

| Endereço | Finalidade |
|---|---|
| `https://cfo.com.br/.well-known/oauth-authorization-server` | Descobrir endpoints e métodos |
| `https://mcp.cfo.com.br/.well-known/oauth-protected-resource/v1` | Metadados do recurso MCP |
| `https://cfo.com.br/.well-known/oauth-protected-resource/mcp` | Metadados do endereço legado `https://cfo.com.br/mcp` |
| `https://cfo.com.br/oauth/register` | Registrar cliente público, sem segredo |
| `https://cfo.com.br/oauth/authorize` | Login e consentimento da pessoa |
| `https://cfo.com.br/oauth/token` | Trocar código ou renovar tokens |
| `https://cfo.com.br/oauth/revoke` | Revogar acesso, sem autenticação de cliente (`none`) |

O servidor também reconhece clientes por **Client ID Metadata Document (CIMD)**:
o `client_id` é a URL HTTPS do documento de identificação do aplicativo. Ele
precisa declarar o mesmo `client_id` e os endereços de retorno registrados.
Endereço de retorno HTTPS fora da lista de callbacks conhecidos precisa estar no
mesmo host do `client_id` ou em um subdomínio dele. A tela de consentimento
mostra esse host como publicador.

O registro dinâmico (**DCR**, sem segredo) permanece disponível para clientes
públicos com endereços de retorno conhecidos, de loopback (`http://localhost`,
`http://127.0.0.1` ou `http://[::1]`) ou de aplicativos instalados
(`cursor:`, `vscode:`, `vscode-insiders:`, `windsurf:`, `zed:`, `claude:` e
`chatgpt:`). Um endereço HTTPS desconhecido não entra na inscrição: se sobrar
algum endereço permitido, o cliente é registrado só com eles e a resposta do
registro traz a lista registrada (RFC 7591 §3.2.1); sem nenhum permitido, a
resposta é `invalid_redirect_uri`. Aplicativo web se identifica por CIMD.

1. Descubra os metadados do recurso e do emissor. Use CIMD ou registre o cliente
   com `redirect_uris`, `response_types: ["code"]` e autenticação `none`.
2. Gere um `code_verifier` aleatório de 43 a 128 caracteres unreserved e calcule
   `code_challenge = base64url(SHA-256(code_verifier))`, sem padding.
3. Abra `/oauth/authorize` com `client_id`, `redirect_uri`, `response_type=code`,
   `code_challenge`, `code_challenge_method=S256`, `scope=mcp`, `state` e `resource`.
   Use o `resource` anunciado no documento de metadados do endereço que você
   chamou: `https://mcp.cfo.com.br/v1` (recomendado) ou, no endereço legado,
   `https://cfo.com.br/mcp`. Os dois dão acesso ao mesmo servidor.
4. A pessoa entra no CFO e permite ou nega. Confira o `state` recebido de volta.
5. Troque o código em `/oauth/token`, por POST form-urlencoded, com
   `grant_type=authorization_code`, `code`, `client_id`, `redirect_uri`,
   `code_verifier` e o mesmo `resource`.
6. Use o access token em cada requisição MCP. Para renovar, envie
   `grant_type=refresh_token`, `refresh_token`, `client_id` e `resource`.

O código tem uso único e expira em 60 segundos. Uma troca recusada também
consome o código. A recusa traz o erro correspondente: `invalid_grant` para
`code_verifier`, `redirect_uri` ou `client_id` de outro aplicativo,
`invalid_target` para `resource` errado e `invalid_client` para `client_id`
inexistente. A tentativa seguinte com o mesmo código, mesmo correta, recebe
`invalid_grant` e exige nova autorização. O access token vale
até uma hora; a autorização pode ser renovada por até 30 dias desde sua concessão.
Cada renovação gira o refresh token sem estender esse prazo. Reutilizar um refresh consumido revoga
a família correspondente e exige nova autorização.

Cada conexão (a mesma conta, o mesmo aplicativo e o mesmo recurso) renova no
máximo 10 vezes por minuto e 300 por dia. O minuto é de relógio, e o dia é o
dia UTC (vira às 21:00 de Brasília). Acima disso a resposta é `429`
`temporarily_unavailable` com `Retry-After` até o fim da janela: até 60 segundos
no minuto e até cerca de 24 horas no dia. O refresh token não é consumido: o
mesmo refresh continua válido depois da espera. Só conta o pedido que giraria o
token (refresh válido, do mesmo aplicativo e do mesmo recurso). O teto é por
conexão, não por IP: outras conexões vindas do mesmo endereço seguem renovando.

O endereço de retorno precisa coincidir com o registrado. Em HTTP de loopback,
a porta pode variar para atender aplicativos locais; host, caminho e query
continuam exatos. HTTPS não admite essa exceção.

## Audiência, limites e revogação

**Tokens OAuth são exclusivos do MCP.** Não são aceitos diretamente em
`/api/v1/*`. Integrações REST devem usar chave de API. Esta restrição substitui
o comportamento antigo que aceitava alguns recursos REST com token OAuth.

O token fica vinculado ao servidor MCP (`https://mcp.cfo.com.br/v1`, o mesmo
do endereço legado `https://cfo.com.br/mcp`). Ele não concede acesso a outro
serviço. Status da conta, vínculo com a organização, plano e bloqueios globais
de IP continuam valendo. A allowlist de IP configurada no painel vale só para
chaves de API na API REST: o MCP não a aplica, nem com token OAuth nem com chave
de API, porque os aplicativos de IA chamam a partir de endereços que variam.

Além das cotas do plano, há limites por credencial/conexão de 120 requisições por
minuto e 5.000 por dia. Respostas `429` incluem `Retry-After`. Credencial
ausente, vencida ou revogada recebe `401` sem penalidade. Credenciais
desconhecidas ou malformadas contam por IP (em IPv6, por bloco /48): acima de
120 em um minuto vindas do mesmo IP, as seguintes recebem `429`. Esse limite
nunca barra uma credencial válida, nem a descoberta sem credencial.

`/oauth/token` e `/oauth/revoke` não limitam o pedido válido por IP: a troca de
código, a renovação e a revogação de um token existente não contam em nenhum
limite por IP (a renovação tem só o teto por conexão descrito acima). Só a falha conta, por IP (em IPv6, por bloco /48) e somando os dois
endpoints: código, refresh ou token desconhecido, malformado ou ausente, e
pedido malformado (método, tipo de conteúdo, `grant_type` inválido, cliente
inexistente). Acima de 30 falhas em um minuto vindas do mesmo IP, as falhas
seguintes recebem `429` com `Retry-After`; o pedido válido desse IP continua
atendido. Código ou refresh token conhecido, mesmo vencido, já usado ou
revogado, não conta e nunca recebe esse `429`: a resposta é o erro exato (como
`invalid_grant`), para o cliente pedir nova autorização. No `/oauth/revoke`,
token desconhecido recebe `200` (RFC 7009) enquanto o IP está abaixo do teto. O
volume total por IP tem um teto de infraestrutura (20 pedidos por segundo,
rajada de 100); acima dele a resposta é `429`.

`/oauth/authorize` e `/oauth/register` aceitam juntos até 60 pedidos por minuto
por IP; acima disso a resposta é `429` com `Retry-After`. O corpo MCP tem
teto de 1 MiB; respostas extensas são recortadas com aviso e JSON válido.
O corpo completo da resposta tem teto de 200 KiB. Identificadores JSON-RPC de
texto aceitam até 256 bytes.

Desconectar em **Painel → Conta → Aplicativos conectados** revoga os tokens e
códigos daquele aplicativo para a sua conta. O acesso termina na chamada seguinte.
Uma nova conexão exige novo consentimento.

| HTTP | Significado |
|---|---|
| `401` | Credencial ausente, inválida, revogada ou vencida; o desafio indica os metadados |
| `403` | A conta, o plano, um bloqueio de IP ou a permissão não permitem o acesso |
| `405` | Método HTTP não suportado |
| `413` | Corpo acima do limite |
| `415` | Tipo de conteúdo incorreto |
| `429` | Limite de uso atingido; respeite `Retry-After` |

```http
WWW-Authenticate: Bearer realm="CFO", scope="mcp",
  resource_metadata="https://mcp.cfo.com.br/.well-known/oauth-protected-resource/v1"
```

O desafio aponta o documento do endereço chamado. No endereço legado
`https://cfo.com.br/mcp`, ele aponta
`https://cfo.com.br/.well-known/oauth-protected-resource/mcp`.

Documentação de consultas: <https://cfo.com.br/docs#mcp>.
