OAuth

Fluxo de autorização do MCP — registro de client, consent no frontend, troca code+verifier por tokens.

O servidor MCP usa OAuth 2.1 com PKCE obrigatório (S256). O consentimento acontece no frontend uPubly — o usuário autoriza com a conta já logada e recebe o código na tela para entregar ao agente.

Discovery

GET https://api.upubly.com/.well-known/oauth-authorization-server
GET https://api.upubly.com/.well-known/oauth-protected-resource

Fluxo

1. Registrar o client (DCR)

POST /oauth/register
Content-Type: application/json

{"client_name": "Meu Agente", "redirect_uris": ["http://localhost:9999/callback"]}

→ 201 {"client_id": "...", ...}. redirect_uris aceita https://, loopback (http://localhost|127.0.0.1) e schemes custom de app; http:// público é rejeitado.

2. Gerar PKCE e abrir a autorização

O agente gera code_verifier (aleatório) e code_challenge = base64url(sha256(verifier)), depois abre no browser:

GET /oauth/authorize?response_type=code&client_id=<id>
    &redirect_uri=<uri>&code_challenge=<challenge>
    &code_challenge_method=S256&state=<state>

A API valida e redireciona (302) para a tela de consent do front:

https://upubly.com/oauth/consent?request=<request_id>

3. Consentimento (usuário logado)

O front consulta os detalhes e aprova/nega com a sessão do usuário:

GET  /oauth/requests/<id>            → detalhes (nome do client, redirect)
POST /oauth/requests/<id>/approve    → {"code": "...", "redirect_to": "..."}
POST /oauth/requests/<id>/deny       → {"redirect_to": "...?error=access_denied"}

redirect_to aponta para o redirect_uri do client com code + state — agentes com listener local recebem automático. A tela também exibe o código para copiar — agentes headless/SSH recebem o código colado pelo usuário. Exibir o code é seguro: sem o code_verifier (que nunca sai do agente) ele não troca por token.

4. Trocar o código por tokens

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=<code>&redirect_uri=<uri>
&client_id=<id>&code_verifier=<verifier>

→ {"access_token": "<JWT>", "refresh_token": "...", "expires_in": 28800}.

5. Refresh

POST /oauth/token
grant_type=refresh_token&refresh_token=<token>&client_id=<id>

Rotação na mesma sessão: o refresh antigo morre, um novo par volta.

6. Revogar

POST /oauth/revoke
token=<refresh_token>&client_id=<id>

Regras de segurança

  • PKCE é mandatório — só S256, plain é rejeitado.
  • O authorization code é single-use e expira em ~10 minutos.
  • redirect_uri deve ser exact match de uma URI registrada.
  • Access token: 8h · Refresh token: 7 dias.
  • Rate limit por IP nos endpoints públicos.