CorsMiddleware.php 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. <?php
  2. namespace Middlewares;
  3. use Psr\Http\Message\ResponseInterface;
  4. use Psr\Http\Message\ServerRequestInterface;
  5. use React\Http\Message\Response;
  6. use React\Promise\PromiseInterface;
  7. /**
  8. * CORS global da API.
  9. *
  10. * Registrado como middleware GLOBAL (no construtor do App em public/index.php),
  11. * portanto roda antes do roteamento. Isso é essencial para responder ao
  12. * preflight `OPTIONS` — que não possui rota registrada e, sem este middleware,
  13. * cairia em 404 antes do navegador concluir a requisição real.
  14. *
  15. * Política atual: liberar todas as origens (`*`). Como usamos "*", NÃO enviamos
  16. * `Access-Control-Allow-Credentials` (o navegador rejeita a combinação) — a
  17. * autenticação é via header `Authorization: Bearer`, não via cookie, então isso
  18. * não é um problema.
  19. */
  20. final class CorsMiddleware
  21. {
  22. public function __invoke(ServerRequestInterface $request, callable $next)
  23. {
  24. // Preflight: responde imediatamente, sem chegar ao roteador/controllers.
  25. if (strtoupper($request->getMethod()) === 'OPTIONS') {
  26. return $this->withCorsHeaders(new Response(204));
  27. }
  28. $response = $next($request);
  29. // Controllers respondem de forma síncrona (Response), mas o FrameworkX
  30. // também aceita Promise. Tratamos ambos para anexar os headers de CORS.
  31. if ($response instanceof PromiseInterface) {
  32. return $response->then(fn (ResponseInterface $resolved): ResponseInterface => $this->withCorsHeaders($resolved));
  33. }
  34. return $this->withCorsHeaders($response);
  35. }
  36. private function withCorsHeaders(ResponseInterface $response): ResponseInterface
  37. {
  38. return $response
  39. ->withHeader('Access-Control-Allow-Origin', '*')
  40. ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS')
  41. ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Unipile-Auth, Unipile-Notify-Secret')
  42. ->withHeader('Access-Control-Max-Age', '86400');
  43. }
  44. }