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¶
- Cliente faz POST /sse/chat com
jwt_tokenno body - Servidor valida JWT e retorna cookie httpOnly:
Set-Cookie: jwt_token=eyJ...; HttpOnly; Secure; SameSite=Lax; Max-Age=3600 - Cliente conecta EventSource - navegador envia cookie automaticamente
- Servidor valida JWT do cookie ou query parameter
Múltiplas Formas de Autenticação¶
O SSE suporta JWT de 3 formas (prioridade):
- Cookie
jwt_token(mais seguro, preferível) - Query Parameter
?token=xyz(fallback para EventSource) - Body no POST (convertido para cookie pelo servidor)
📋 Checklist de Deploy¶
Desenvolvimento Local¶
- [ ]
.envcomWEBCHAT_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
.envcom 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¶
1. Cookie não está sendo enviado¶
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.com → api.theifriend.com
Solução: Ambos os domínios devem estar em WEBCHAT_ALLOWED_ORIGINS
📚 Referências¶
💡 Dicas de Segurança¶
- Sempre use HTTPS em produção -
Securecookie flag - HttpOnly - previne acesso via JavaScript (XSS)
- SameSite=Lax - protege contra CSRF
- TTL curto - cookies expiram em 1 hora
- Validar JWT - sempre validar assinatura e expiração
- Origens específicas - nunca use
*em produção
Última atualização: 15/02/2026