Skip to content

CORS, SSE e Cookies - Guia de Configuração

🔴 Problema: CORS com Credentials

Quando usamos credentials: 'include' no fetch/EventSource (necessário para enviar/receber cookies), o navegador impõe restrições mais rigorosas de CORS:

Access-Control-Allow-Origin: * ❌ NÃO PERMITIDO com credentials
Access-Control-Allow-Origin: https://seudominio.com ✅ PERMITIDO

Erro Típico

Access to fetch at 'https://api.theifriend.com/sse/chat' from origin 'https://app.theifriend.com' 
has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the 
response must not be the wildcard '*' when the request's credentials mode is 'include'.

✅ Solução Implementada

1. Configuração do Backend (unified_bot.py)

# ❌ ANTES (não funciona com credentials)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Wildcard não permitido!
    allow_credentials=True,
)

# ✅ DEPOIS (funciona)
allowed_origins = [
    "http://localhost:3000",
    "https://app.theifriend.com",
    "https://www.theifriend.com",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=allowed_origins,  # Origens específicas
    allow_credentials=True,
)

2. Configuração do .env

# ❌ NÃO USE wildcard com credentials
WEBCHAT_ALLOWED_ORIGINS=*

# ✅ USE origens específicas (separadas por vírgula)
WEBCHAT_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080,https://app.theifriend.com,https://www.theifriend.com

3. Configuração do Frontend

// POST /sse/chat
fetch('https://api.theifriend.com/sse/chat', {
  method: 'POST',
  credentials: 'include',  // Necessário para cookies
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    jwt_token: 'eyJ...',  // Servidor retorna cookie httpOnly
    message: 'Olá'
  })
});

// EventSource envia cookie automaticamente
const eventSource = new EventSource('https://api.theifriend.com/sse/stream/xyz');
// Cookie jwt_token é enviado automaticamente pelo navegador

🔐 Autenticação JWT com SSE

Fluxo Completo

  1. Cliente faz POST /sse/chat com jwt_token no body
  2. Servidor valida JWT e retorna cookie httpOnly:
    Set-Cookie: jwt_token=eyJ...; HttpOnly; Secure; SameSite=Lax; Max-Age=3600
    
  3. Cliente conecta EventSource - navegador envia cookie automaticamente
  4. Servidor valida JWT do cookie ou query parameter

Múltiplas Formas de Autenticação

O SSE suporta JWT de 3 formas (prioridade):

  1. Cookie jwt_token (mais seguro, preferível)
  2. Query Parameter ?token=xyz (fallback para EventSource)
  3. Body no POST (convertido para cookie pelo servidor)

📋 Checklist de Deploy

Desenvolvimento Local

  • [ ] .env com WEBCHAT_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080
  • [ ] Frontend rodando em http://localhost:3000
  • [ ] Backend rodando em http://localhost:8080

Staging/Produção

  • [ ] Adicionar domínio Vercel: https://app-xxx.vercel.app
  • [ ] Adicionar domínio produção: https://app.theifriend.com
  • [ ] Atualizar .env com todas as origens
  • [ ] Rebuild e redeploy do backend
  • [ ] Testar com DevTools > Network > Headers

Verificação do CORS

# Teste manual com curl
curl -H "Origin: https://app.theifriend.com" \
     -H "Access-Control-Request-Method: POST" \
     -H "Access-Control-Request-Headers: Content-Type" \
     -X OPTIONS \
     https://api.theifriend.com/sse/chat

# Deve retornar:
# Access-Control-Allow-Origin: https://app.theifriend.com
# Access-Control-Allow-Credentials: true

🚨 Problemas Comuns

Sintoma: EventSource conecta, mas servidor diz "JWT não fornecido"

Causas: - Cookie não foi configurado pelo servidor (verificar response do POST) - SameSite incorreto (deve ser Lax ou None com Secure) - Domínio diferente sem credentials: 'include'

Solução: Verificar no DevTools > Application > Cookies

2. CORS ainda bloqueando

Sintoma: Erro de CORS no console

Causas: - Origem não está na lista WEBCHAT_ALLOWED_ORIGINS - Servidor não foi reiniciado após atualizar .env - Nginx/Proxy sobrescrevendo headers CORS

Solução:

# Verificar logs do servidor
docker logs ifriend-openai-api | grep "CORS origens permitidas"

# Deve mostrar:
# 🌐 CORS origens permitidas: ['https://app.theifriend.com', ...]

3. EventSource com subdomínios

Problema: app.theifriend.comapi.theifriend.com

Solução: Ambos os domínios devem estar em WEBCHAT_ALLOWED_ORIGINS

📚 Referências

💡 Dicas de Segurança

  1. Sempre use HTTPS em produção - Secure cookie flag
  2. HttpOnly - previne acesso via JavaScript (XSS)
  3. SameSite=Lax - protege contra CSRF
  4. TTL curto - cookies expiram em 1 hora
  5. Validar JWT - sempre validar assinatura e expiração
  6. Origens específicas - nunca use * em produção

Última atualização: 15/02/2026