I'm using Mercure to handle real-time updates in my Symfony project, and I have a frontend running on http://localhost:8083. However, when trying to subscribe to Mercure events, I get the following CORS error:
Access to XMLHttpRequest at 'http://localhost:12345/.well-known/mercure?topic=messages' from origin 'http://localhost:8083' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
My docker set up:
mercure:
image: dunglas/mercure
restart: always
environment:
- MERCURE_PUBLISHER_JWT_KEY="!MySecretKeyMercureHubJWTSecretKey!"
- MERCURE_SUBSCRIBER_JWT_KEY="!MySecretKeyMercureHubJWTSecretKey!"
- MERCURE_JWT_SECRET="!MySecretKeyMercureHubJWTSecretKey!"
- MERCURE_CORS_ALLOWED_ORIGINS=http://localhost:8083
- SERVER_NAME=:80
healthcheck:
test: [ "CMD", "wget", "-q", "--spider", "https://localhost/healthz" ]
timeout: 5s
retries: 5
start_period: 60s
ports:
- "12345:80"
volumes:
- mercure_data:/data
- mercure_config:/config
Frontend reactnative (EventSource Subscription)
const token = "!MySecretKeyMercureHubJWTSecretKey!";
const url = new URL("http://localhost:12345/.well-known/mercure");
url.searchParams.append("topic", "messages");
const eventSource = new EventSource(url, {
headers: {
Authorization: `Bearer ${token}`
}
});
eventSource.addEventListener("open", () => {
console.log("Open SSE connection.");
});
.env
MERCURE_URL=http://mercure/.well-known/mercure
MERCURE_PUBLIC_URL=http://localhost:12345/.well-known/mercure
MERCURE_JWT_SECRET=!MySecretKeyMercureHubJWTSecretKey!
mercure.yaml:
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%'
public_url: '%env(MERCURE_PUBLIC_URL)%'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: ['*']
subscribe: ['*']
the backend controller
#[Route('/messages', name: 'post_message', methods: ['POST'])] class MessageController extends AbstractController
{
public function __invoke(Request $request, EntityManagerInterface $em, HubInterface $hub, SerializerInterface $serializer): JsonResponse
{
$data = json_decode($request->getContent(), true);
if (!isset($data['content'])) {
return new JsonResponse(['error' => 'Content is required'], 400);
}
$message = new Message();
$message->setContent($data['content']);
$em->persist($message);
$em->flush();
$update = new Update('messages', json_encode(['content' => $message->getContent()]));
$hub->publish($update);
return new JsonResponse($serializer->normalize($message), 201);
}
}