Files
agent_platform_oci/Implementando_Basic_Auth.md

20 KiB

Implementando Basic Auth

Para validar todo o circuito com Basic Auth, você precisa configurar três relações distintas:

Cliente de teste
   └─ Basic Auth A ─► Agent Gateway :8010
                         └─ Basic Auth B ─► Agent Backend :8000
                                                └─ Basic Auth C ─► MCP Gateway :8300

Há um detalhe importante: no pacote atual, a autenticação Basic já funciona para chamadas de entrada, mas os clientes internos ainda não enviam Basic Auth:

  • Agent Gateway → Agent Backend não envia credencial;
  • Agent Backend → MCP Gateway envia apenas Bearer Token.

Portanto, para testar o circuito inteiro com Basic Auth, faça os dois pequenos ajustes de código descritos abaixo.


1. Preparar o ambiente

Considere que o ZIP foi extraído em:

cd agent_framework_oci_authentication_v2_1

Crie um único ambiente virtual para facilitar o teste:

python -m venv .venv
source .venv/bin/activate

No Windows PowerShell:

python -m venv .venv
.\.venv\Scripts\Activate.ps1

Instale o framework e as dependências dos três componentes:

pip install -U pip

pip install -e ./libs/agent_framework

pip install \
  -r ./Tuning-Performance/Authentication/agent_template_backend_authentication/requirements.txt \
  -r ./apps/agent_gateway/requirements.txt \
  -r ./apps/mcp_gateway/requirements.txt

Confirme a importação:

python -c "from agent_framework.security import install_authentication; print('framework ok')"

2. Criar três pares de Client ID e Secret

Use credenciais diferentes para cada trecho. Para teste local:

Fluxo Client ID Secret de teste
Cliente → Agent Gateway tia-test TiaGateway-Test-2026!
Agent Gateway → Backend agent-gateway-test GatewayBackend-Test-2026!
Backend → MCP Gateway agent-backend-test BackendMcp-Test-2026!

Esses valores são apenas para ambiente local. Não os reutilize em produção.

Gerar os hashes

O script está em:

Tuning-Performance/Authentication/
  agent_template_backend_authentication/
    scripts/generate_secret_hash.py

Execute:

python Tuning-Performance/Authentication/agent_template_backend_authentication/scripts/generate_secret_hash.py \
  --secret 'TiaGateway-Test-2026!'

Depois:

python Tuning-Performance/Authentication/agent_template_backend_authentication/scripts/generate_secret_hash.py \
  --secret 'GatewayBackend-Test-2026!'

E:

python Tuning-Performance/Authentication/agent_template_backend_authentication/scripts/generate_secret_hash.py \
  --secret 'BackendMcp-Test-2026!'

Você receberá três valores semelhantes a:

pbkdf2_sha256:310000:<salt>:<digest>

Guarde-os temporariamente:

HASH_CLIENT_GATEWAY='pbkdf2_sha256:310000:...'
HASH_GATEWAY_BACKEND='pbkdf2_sha256:310000:...'
HASH_BACKEND_MCP='pbkdf2_sha256:310000:...'

O hash muda a cada execução porque o salt é aleatório. Isso é esperado.


3. Configurar o Agent Gateway

Entre no diretório:

cd apps/agent_gateway

Copie o exemplo:

cp .env.example .env

Adicione ao final do .env:

# Entrada: cliente/TIA -> Agent Gateway
AGENT_GATEWAY_AUTH_ENABLED=true
AGENT_GATEWAY_AUTH_MODE=basic
AGENT_GATEWAY_AUTH_BASIC_CLIENT_ID=tia-test
AGENT_GATEWAY_AUTH_BASIC_SECRET_HASH=COLE_AQUI_HASH_CLIENT_GATEWAY
AGENT_GATEWAY_AUTH_BASIC_REALM=agent-gateway

AGENT_GATEWAY_AUTH_PUBLIC_PATHS=/health,/docs,/openapi.json,/redoc
AGENT_GATEWAY_AUTH_PUBLIC_PREFIXES=

# Saída: Agent Gateway -> Agent Backend
BACKEND_AUTH_MODE=basic
BACKEND_AUTH_CLIENT_ID=agent-gateway-test
BACKEND_AUTH_SECRET=GatewayBackend-Test-2026!

Não coloque aspas no .env:

BACKEND_AUTH_SECRET=GatewayBackend-Test-2026!

O arquivo de backends já aponta o backend Contas para:

contas:
  url: http://localhost:8000

Arquivo:

apps/agent_gateway/config/backends.yaml

Para este teste, mantenha apenas o backend contas ou force o backend no payload. Caso contrário, pedidos sobre ofertas e suporte podem ser roteados para portas em que nenhum backend está rodando.


4. Fazer o Agent Gateway enviar Basic Auth ao backend

Abra:

libs/agent_framework/src/agent_framework/global_supervisor/client.py

Substitua a classe BackendClient por uma versão que aceite autenticação Basic.

No início do arquivo, adicione:

import os

Altere o construtor:

class BackendClient:
    def __init__(
        self,
        timeout_seconds: float = 120.0,
        basic_client_id: str | None = None,
        basic_secret: str | None = None,
    ):
        self.timeout_seconds = timeout_seconds
        self.basic_client_id = basic_client_id
        self.basic_secret = basic_secret

    def _auth(self) -> httpx.BasicAuth | None:
        if self.basic_client_id and self.basic_secret:
            return httpx.BasicAuth(
                username=self.basic_client_id,
                password=self.basic_secret,
            )
        return None

No método call_message, troque:

resp = await client.post(url, json=payload)

por:

resp = await client.post(
    url,
    json=payload,
    auth=self._auth(),
)

No método health, você pode manter /health público. Caso queira enviar autenticação também, use:

resp = await client.get(url, auth=self._auth())

Agora abra:

apps/agent_gateway/app/main.py

Adicione:

import os

Troque:

backend_client = BackendClient(
    timeout_seconds=settings.BACKEND_TIMEOUT_SECONDS
)

por:

backend_client = BackendClient(
    timeout_seconds=settings.BACKEND_TIMEOUT_SECONDS,
    basic_client_id=os.getenv("BACKEND_AUTH_CLIENT_ID"),
    basic_secret=os.getenv("BACKEND_AUTH_SECRET"),
)

Isso implementa:

Agent Gateway → Agent Backend
Authorization: Basic base64(agent-gateway-test:GatewayBackend-Test-2026!)

5. Configurar o Agent Backend autenticado

Entre no diretório:

cd Tuning-Performance/Authentication/agent_template_backend_authentication

Copie o exemplo:

cp .env.example .env

Ajuste a seção de autenticação:

# Entrada: Agent Gateway -> Agent Backend
AGENT_AUTH_ENABLED=true
AGENT_AUTH_MODE=basic
AGENT_AUTH_BASIC_CLIENT_ID=agent-gateway-test
AGENT_AUTH_BASIC_SECRET_HASH=COLE_AQUI_HASH_GATEWAY_BACKEND
AGENT_AUTH_BASIC_REALM=agent-contas

AGENT_AUTH_PUBLIC_PATHS=/health,/docs,/openapi.json,/redoc
AGENT_AUTH_PUBLIC_PREFIXES=

Para usar o MCP Gateway:

MCP_GATEWAY_ENABLED=true
MCP_GATEWAY_URL=http://localhost:8300
MCP_GATEWAY_TIMEOUT_SECONDS=60

# Saída: Agent Backend -> MCP Gateway
MCP_GATEWAY_AUTH_MODE=basic
MCP_GATEWAY_BASIC_CLIENT_ID=agent-backend-test
MCP_GATEWAY_BASIC_SECRET=BackendMcp-Test-2026!

Para evitar dependências externas durante o primeiro teste, configure também:

LLM_PROVIDER=mock
ENABLE_LANGFUSE=false
ENABLE_ANALYTICS=false

SESSION_REPOSITORY_PROVIDER=memory
MEMORY_REPOSITORY_PROVIDER=memory
CHECKPOINT_REPOSITORY_PROVIDER=memory
CACHE_PROVIDER=memory
USAGE_REPOSITORY_PROVIDER=memory

Os nomes exatos de alguns providers podem depender do arquivo de configuração atual do framework. Caso o .env.example já contenha valores locais ou mock, preserve-os.


6. Fazer o Backend enviar Basic Auth ao MCP Gateway

Abra:

libs/agent_framework/src/agent_framework/gateways/mcp_gateway_client.py

Substitua a implementação por:

from __future__ import annotations

import base64
from typing import Any

import httpx


class MCPGatewayClient:
    def __init__(
        self,
        base_url: str,
        token: str | None = None,
        timeout_seconds: int = 60,
        auth_mode: str | None = None,
        basic_client_id: str | None = None,
        basic_secret: str | None = None,
    ):
        self.base_url = base_url.rstrip("/")
        self.token = token
        self.timeout_seconds = timeout_seconds
        self.auth_mode = (auth_mode or "").strip().lower()
        self.basic_client_id = basic_client_id
        self.basic_secret = basic_secret

    def _headers(self) -> dict[str, str]:
        if (
            self.auth_mode == "basic"
            and self.basic_client_id
            and self.basic_secret
        ):
            raw = f"{self.basic_client_id}:{self.basic_secret}".encode("utf-8")
            encoded = base64.b64encode(raw).decode("ascii")
            return {"Authorization": f"Basic {encoded}"}

        if self.token:
            return {"Authorization": f"Bearer {self.token}"}

        return {}

    async def list_tools(self) -> dict[str, Any]:
        async with httpx.AsyncClient(
            timeout=self.timeout_seconds
        ) as client:
            response = await client.get(
                f"{self.base_url}/v1/tools",
                headers=self._headers(),
            )
            response.raise_for_status()
            return response.json()

    async def invoke_tool(
        self,
        *,
        tenant_id: str,
        agent_id: str,
        channel: str | None,
        tool_name: str,
        arguments: dict[str, Any] | None = None,
        business_context: dict[str, Any] | None = None,
        metadata: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        payload = {
            "tenant_id": tenant_id,
            "agent_id": agent_id,
            "channel": channel,
            "tool_name": tool_name,
            "arguments": arguments or {},
            "business_context": business_context or {},
            "metadata": metadata or {},
        }

        async with httpx.AsyncClient(
            timeout=self.timeout_seconds
        ) as client:
            response = await client.post(
                f"{self.base_url}/v1/tools/{tool_name}/invoke",
                json=payload,
                headers=self._headers(),
            )
            response.raise_for_status()
            return response.json()

Agora abra:

libs/agent_framework/src/agent_framework/mcp/tool_router.py

Localize:

MCPGatewayClient(
    base_url=getattr(
        settings,
        "MCP_GATEWAY_URL",
        "http://localhost:8300",
    ),
    token=getattr(settings, "MCP_GATEWAY_TOKEN", None),
    timeout_seconds=getattr(
        settings,
        "MCP_GATEWAY_TIMEOUT_SECONDS",
        settings.MCP_TOOL_TIMEOUT_SECONDS,
    ),
)

Altere para:

MCPGatewayClient(
    base_url=getattr(
        settings,
        "MCP_GATEWAY_URL",
        "http://localhost:8300",
    ),
    token=getattr(settings, "MCP_GATEWAY_TOKEN", None),
    timeout_seconds=getattr(
        settings,
        "MCP_GATEWAY_TIMEOUT_SECONDS",
        settings.MCP_TOOL_TIMEOUT_SECONDS,
    ),
    auth_mode=getattr(
        settings,
        "MCP_GATEWAY_AUTH_MODE",
        None,
    ),
    basic_client_id=getattr(
        settings,
        "MCP_GATEWAY_BASIC_CLIENT_ID",
        None,
    ),
    basic_secret=getattr(
        settings,
        "MCP_GATEWAY_BASIC_SECRET",
        None,
    ),
)

Adicione estes campos em:

libs/agent_framework/src/agent_framework/config/settings.py

Próximo das configurações existentes de MCP Gateway:

MCP_GATEWAY_AUTH_MODE: str | None = None
MCP_GATEWAY_BASIC_CLIENT_ID: str | None = None
MCP_GATEWAY_BASIC_SECRET: str | None = None

Há também uma factory local em:

Tuning-Performance/Authentication/
  agent_template_backend_authentication/
    app/mcp_gateway_client_factory.py

Ajuste para:

from __future__ import annotations

import os

from agent_framework.gateways import MCPGatewayClient


def build_mcp_gateway_client() -> MCPGatewayClient | None:
    if os.getenv("MCP_GATEWAY_ENABLED", "true").lower() != "true":
        return None

    return MCPGatewayClient(
        base_url=os.getenv(
            "MCP_GATEWAY_URL",
            "http://localhost:8300",
        ),
        token=os.getenv("MCP_GATEWAY_TOKEN") or None,
        timeout_seconds=int(
            os.getenv("MCP_GATEWAY_TIMEOUT_SECONDS", "60")
        ),
        auth_mode=os.getenv("MCP_GATEWAY_AUTH_MODE"),
        basic_client_id=os.getenv(
            "MCP_GATEWAY_BASIC_CLIENT_ID"
        ),
        basic_secret=os.getenv(
            "MCP_GATEWAY_BASIC_SECRET"
        ),
    )

7. Configurar o MCP Gateway

Entre no diretório:

cd apps/mcp_gateway

Crie .env:

cp .env.example .env

Adicione:

# Entrada: Agent Backend -> MCP Gateway
MCP_GATEWAY_AUTH_ENABLED=true
MCP_GATEWAY_AUTH_MODE=basic
MCP_GATEWAY_AUTH_BASIC_CLIENT_ID=agent-backend-test
MCP_GATEWAY_AUTH_BASIC_SECRET_HASH=COLE_AQUI_HASH_BACKEND_MCP
MCP_GATEWAY_AUTH_BASIC_REALM=mcp-gateway

MCP_GATEWAY_AUTH_PUBLIC_PATHS=/health,/ready,/docs,/openapi.json,/redoc
MCP_GATEWAY_AUTH_PUBLIC_PREFIXES=

MCP_GATEWAY_CONFIG_PATH=config/mcp_gateway.yaml

Desabilitar o mecanismo Bearer legado

O MCP Gateway ainda possui um segundo mecanismo antigo, configurado dentro de:

apps/mcp_gateway/config/mcp_gateway.yaml

Localize a seção:

auth:
  enabled: true

Altere para:

auth:
  enabled: false

Isso é necessário porque o novo middleware já faz a autenticação Basic. Caso o auth_check() legado continue habilitado, a requisição passará pelo Basic e depois será rejeitada por não possuir Bearer Token.


8. Subir os componentes

Use quatro terminais.

Terminal 1 — MCP Servers

O MCP Gateway precisa ter pelo menos um servidor MCP disponível para demonstrar uma chamada real.

Na raiz do projeto:

source .venv/bin/activate

Suba o servidor telecom:

uvicorn mcp.servers.telecom_mcp_server.main:app \
  --host 0.0.0.0 \
  --port 8100 \
  --reload

Em outro terminal, caso queira também o retail:

uvicorn mcp.servers.retail_mcp_server.main:app \
  --host 0.0.0.0 \
  --port 8200 \
  --reload

Confira as URLs configuradas em:

apps/mcp_gateway/config/mcp_gateway.yaml

Para execução local, devem apontar para:

url: http://localhost:8100

e:

url: http://localhost:8200

Terminal 2 — MCP Gateway

cd apps/mcp_gateway
source ../../.venv/bin/activate

Suba usando --env-file. Isso é importante porque o middleware lê variáveis com os.getenv():

uvicorn app.main:app \
  --host 0.0.0.0 \
  --port 8300 \
  --reload \
  --env-file .env

Teste a saúde pública:

curl http://localhost:8300/health

Teste um endpoint protegido sem credencial:

curl -i http://localhost:8300/v1/tools

Esperado:

HTTP/1.1 401 Unauthorized

Teste com Basic Auth:

curl -i \
  -u 'agent-backend-test:BackendMcp-Test-2026!' \
  http://localhost:8300/v1/tools

Esperado:

HTTP/1.1 200 OK

Terminal 3 — Agent Backend

cd Tuning-Performance/Authentication/agent_template_backend_authentication
source ../../../.venv/bin/activate

Suba:

uvicorn app.main:app \
  --host 0.0.0.0 \
  --port 8000 \
  --reload \
  --env-file .env

Teste saúde:

curl http://localhost:8000/health

Teste endpoint protegido sem credencial:

curl -i http://localhost:8000/agents

Esperado:

HTTP/1.1 401 Unauthorized

Teste com a credencial usada pelo Agent Gateway:

curl -i \
  -u 'agent-gateway-test:GatewayBackend-Test-2026!' \
  http://localhost:8000/agents

Esperado:

HTTP/1.1 200 OK

Teste mensagem diretamente:

curl -X POST http://localhost:8000/gateway/message \
  -u 'agent-gateway-test:GatewayBackend-Test-2026!' \
  -H 'Content-Type: application/json' \
  -d '{
    "channel": "web",
    "agent_id": "telecom_contas",
    "tenant_id": "default",
    "payload": {
      "text": "Quero consultar minha fatura",
      "session_id": "teste-backend-001",
      "user_id": "user-001",
      "customer_id": "12345",
      "message_id": "msg-001"
    }
  }'

Terminal 4 — Agent Gateway

cd apps/agent_gateway
source ../../.venv/bin/activate

Suba:

uvicorn app.main:app \
  --host 0.0.0.0 \
  --port 8010 \
  --reload \
  --env-file .env

Teste saúde:

curl http://localhost:8010/health

Teste endpoint protegido sem credencial:

curl -i http://localhost:8010/backends

Esperado:

HTTP/1.1 401 Unauthorized

Teste com a credencial externa:

curl -i \
  -u 'tia-test:TiaGateway-Test-2026!' \
  http://localhost:8010/backends

Esperado:

HTTP/1.1 200 OK

9. Validar o circuito completo

Force o backend contas para evitar que o roteador selecione um backend não iniciado:

curl -X POST http://localhost:8010/gateway/message \
  -u 'tia-test:TiaGateway-Test-2026!' \
  -H 'Content-Type: application/json' \
  -d '{
    "channel": "web",
    "backend_id": "contas",
    "tenant_id": "default",
    "agent_id": "telecom_contas",
    "session_id": "circuito-basic-001",
    "payload": {
      "text": "Quero consultar minha fatura",
      "session_id": "circuito-basic-001",
      "user_id": "user-001",
      "customer_id": "12345",
      "message_id": "msg-circuito-001"
    }
  }'

O circuito esperado é:

curl
  │ Basic tia-test
  ▼
Agent Gateway :8010
  │ Basic agent-gateway-test
  ▼
Agent Backend :8000
  │ Basic agent-backend-test
  ▼
MCP Gateway :8300
  ▼
MCP Server :8100 ou :8200

10. Como comprovar cada autenticação

Faça testes negativos em cada trecho.

Secret externo incorreto

curl -i \
  -u 'tia-test:senha-errada' \
  http://localhost:8010/backends

Resultado esperado:

401 Unauthorized

Secret do gateway para backend incorreto

Altere temporariamente no apps/agent_gateway/.env:

BACKEND_AUTH_SECRET=senha-errada

Reinicie o Agent Gateway e envie uma mensagem.

O gateway deverá retornar erro de backend, normalmente:

502 Bad Gateway

O erro interno será originado por um:

401 Unauthorized

do Agent Backend.

Secret do backend para MCP incorreto

Altere temporariamente:

MCP_GATEWAY_BASIC_SECRET=senha-errada

Reinicie o backend e execute uma frase que acione uma ferramenta MCP.

O backend deverá registrar falha na chamada ao MCP Gateway com:

401 Unauthorized

11. Verificação rápida de portas

No Linux ou WSL:

ss -lntp | grep -E ':8000|:8010|:8100|:8200|:8300'

No Windows PowerShell:

Get-NetTCPConnection -State Listen |
  Where-Object LocalPort -in 8000,8010,8100,8200,8300 |
  Sort-Object LocalPort

Você deverá ver:

8000  Agent Backend
8010  Agent Gateway
8100  Telecom MCP Server
8200  Retail MCP Server
8300  MCP Gateway

Observação importante

O segredo original precisa existir no componente cliente:

TIA ou curl:
  TiaGateway-Test-2026!

Agent Gateway:
  GatewayBackend-Test-2026!

Agent Backend:
  BackendMcp-Test-2026!

Os componentes servidores armazenam apenas os hashes:

Agent Gateway:
  hash de TiaGateway-Test-2026!

Agent Backend:
  hash de GatewayBackend-Test-2026!

MCP Gateway:
  hash de BackendMcp-Test-2026!

Em produção, os segredos originais e hashes devem vir de Vault ou Kubernetes Secret, não de arquivos .env.