# Plano de Implementacao: Bearer Token Fixo (API Key) via Env Var

## Visao Geral

Adicionar classe `DualTokenVerifier` em `server.py` que valida tokens `tk_` via hash SHA-256 em env var, delegando JWTs ao `JWTVerifier` existente. Unico arquivo modificado: `server.py` (~30 linhas).

## Fase 1: Implementar DualTokenVerifier

Adicionar autenticacao dual no `server.py` sem alterar nenhuma tool.

### Tarefas

- [x] Adicionar imports: `hashlib`, `TokenVerifier`, `AccessToken`, `JWTVerifier`
- [x] Criar classe `DualTokenVerifier(TokenVerifier)` com `verify_token()`
- [x] Alterar `create_mcp_server()` para usar `DualTokenVerifier` quando auth habilitada
- [x] Rodar `ruff check --fix . && ruff format . && mypy .`

### Detalhes Tecnicos

**Imports adicionais em `server.py`:**
```python
import hashlib
from fastmcp.server.auth import AccessToken, TokenVerifier
from fastmcp.server.auth.providers.jwt import JWTVerifier
```

**Classe `DualTokenVerifier`:**
```python
class DualTokenVerifier(TokenVerifier):
    """Valida tokens tk_ via hash em env var, ou delega JWT ao JWTVerifier."""

    def __init__(self, jwt_verifier: JWTVerifier) -> None:
        super().__init__()
        self.jwt_verifier = jwt_verifier
        self.api_keys: dict[str, dict[str, str]] = {}
        self._load_api_keys()

    def _load_api_keys(self) -> None:
        """Carrega API keys de env var API_KEY_HASHES.

        Formato: sha256hash:user_id:email[,sha256hash2:user_id2:email2]
        """
        raw = os.getenv("API_KEY_HASHES", "")
        for entry in raw.split(","):
            entry = entry.strip()
            if not entry:
                continue
            parts = entry.split(":")
            if len(parts) == 3:
                token_hash, user_id, email = parts
                self.api_keys[token_hash] = {"user_id": user_id, "email": email}
        if self.api_keys:
            logger.info("Carregadas %d API keys fixas", len(self.api_keys))

    async def verify_token(self, token: str) -> AccessToken | None:
        if token.startswith("tk_"):
            token_hash = hashlib.sha256(token.encode()).hexdigest()
            key_data = self.api_keys.get(token_hash)
            if not key_data:
                return None
            return AccessToken(
                token=token,
                client_id=key_data["user_id"],
                scopes=[],
                expires_at=None,
                claims={"sub": key_data["user_id"], "email": key_data["email"]},
            )
        return await self.jwt_verifier.verify_token(token)
```

**Nota sobre `super().__init__()`:** `TokenVerifier.__init__` aceita `base_url: str | None = None` e `required_scopes: list[str] | None = None`. Nao precisamos passar nada — o SupabaseProvider ja cuida disso.

**Alteracao em `create_mcp_server()` (bloco `if auth_enabled`):**
```python
# Substituir:
auth = SupabaseProvider(
    project_url=supabase_url,
    base_url=base_url,
)

# Por:
jwt_verifier = JWTVerifier(
    jwks_uri=f"{supabase_url}/auth/v1/.well-known/jwks.json",
    issuer=f"{supabase_url}/auth/v1",
    algorithm="ES256",
)
dual_verifier = DualTokenVerifier(jwt_verifier)
auth = SupabaseProvider(
    project_url=supabase_url,
    base_url=base_url,
    token_verifier=dual_verifier,
)
```

**Arquivos envolvidos:**
- `server.py` (linhas 8-13 imports, linhas 95-112 create_mcp_server)

**Por que funciona sem alterar tools:**
- `_get_current_user()` em `tools/_helpers.py:59-73` usa `get_access_token().claims.get("sub")` e `.get("email")`
- O `AccessToken` retornado por `DualTokenVerifier.verify_token()` preenche `claims` com `sub` e `email`
- Todas as 12 tools usam `_get_current_user()` — nenhuma precisa mudar

## Fase 2: Configurar e Testar

Deploy no Railway e validacao end-to-end.

### Tarefas

- [x] Gerar token `tk_` e hash SHA-256 localmente
- [x] Configurar env var `API_KEY_HASHES` no Railway
- [x] Deploy no Railway
- [x] Testar OAuth existente (nao deve quebrar)
- [x] Testar API key `tk_` (initialize + whoami + listar_processos)
- [x] Testar token invalido (deve retornar 401)

### Detalhes Tecnicos

**Gerar token (local, uma vez):**
```python
import secrets, hashlib
token = f"tk_{secrets.token_urlsafe(48)}"
token_hash = hashlib.sha256(token.encode()).hexdigest()
print(f"Token: {token}")
print(f"Hash:  {token_hash}")
```

**Env var no Railway:**
```
API_KEY_HASHES=<hash>:76ba8190-b43d-4665-84cd-0c30f3bc7b8e:marcosmarf27@gmail.com
```

**Teste OAuth (deve continuar funcionando):**
```bash
ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
TOKEN=$(curl -s -X POST "https://pglghyznirxkewcmuhvp.supabase.co/auth/v1/token?grant_type=password" \
  -H "apikey: $ANON_KEY" -H "Content-Type: application/json" \
  -d '{"email":"marcosmarf27@gmail.com","password":"224351*27"}' \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
# Initialize + notification + tools/list (fluxo completo)
```

**Teste API key:**
```bash
curl -s -X POST https://mcp.tecjustica.com/mcp \
  -H "Authorization: Bearer tk_<token>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"manus","version":"1.0"}}}'
```

**Configurar no Manus:**
```
URL: https://mcp.tecjustica.com/mcp
Header: Authorization: Bearer tk_<o_token_gerado>
```
