Question
Web BroadcastChannel fails in iframe when src is redirected
I have an iframe with src attr set to https://example.com/path. This URL redirects 301 to https://othersite.com/path. This page loads some JS and creates a BroadcastChannel with a listener on it. It then opens a new window to https://othersite.com/path2. This page load some JS that creates a BroadcastChannel with the same name and does a postMessage. The problem: it never arrives in the iframe. The weird thing is, it does work if the src of the iframe is not a redirect. But that doesn't make sense because even though it is redirected, the origin is determined by the page that is eventually loaded. So the origins are the same (yes I checked that), and the BroadcastChannel names are the same. So why doesn't this work??
Edit: I have found a reproduction scenario:
Ok, I have a reproduction scenario. Run this code on your machine (install express first npm init
and npm i express
). Then add the following hosts to your /etc/hosts
(the domains are important, it seems to work when they are subdomains of the same domain): 127.0.0.1 api.rubbish.wow accounts.rubbish.wow google.wow my.rubbish.wow my.site.wow
and run this:
const express = require('express');
// Server 1 (Port 4000)
const server1 = express();
server1.get('/', (req, res) => {
res.send(`
<html>
<body>
<iframe src="http://api.rubbish.wow:4001" width="600" height="400"></iframe>
</body>
</html>
`);
});
server1.listen(4000, () => {
console.log('Server 1 listening on port 4000');
});
// Server 2 (Port 4001)
const server2 = express();
server2.get('/', (req, res) => {
res.redirect(301, 'http://accounts.rubbish.wow:4002/iframe-content');
});
server2.listen(4001, () => {
console.log('Server 2 listening on port 4001');
});
// Server 3 (Port 4002)
const server3 = express();
server3.get('/iframe-content', (req, res) => {
res.send(`
<html>
<body>
<h1>Iframe Content</h1>
<div id="messages"></div>
<button onclick="openNewWindow()">Open New Window</button>
<script>
function openNewWindow() {
window.open('http://google.wow:4003');
}
const channel = new BroadcastChannel('auth');
channel.onmessage = (event) => {
const messageDiv = document.getElementById('messages');
const newMessage = document.createElement('div');
newMessage.textContent = 'Received message: ' + event.data;
messageDiv.appendChild(newMessage);
};
</script>
</body>
</html>
`);
});
server3.get('/new-window', (req, res) => {
res.send(`
<html>
<body>
<h1>New Window</h1>
<button id="sendMessageButton">Send Message</button>
<script>
const channel = new BroadcastChannel('auth');
document.getElementById('sendMessageButton').addEventListener('click', () => {
channel.postMessage('Authentication successful');
});
</script>
</body>
</html>
`);
});
server3.listen(4002, () => {
console.log('Server 3 listening on port 4002');
});
// Server 4 (Port 4003)
const server4 = express();
server4.get('/', (req, res) => {
res.redirect('http://accounts.rubbish.wow:4002/new-window');
});
server4.listen(4003, () => {
console.log('Server 4 listening on port 4003');
});
Open http://my.site.wow:4000 in your browser, open the window and try to send the message -> FAIL Open http://my.rubbish.wow:4000 in your browser, open the window and try to send the message -> SUCCESS
So when origin of the iframe and the origin of the top level page are both subdomains of the same domain (rubbish.wow) it works, otherwise it doesn't and I don't have a clue why not.