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.

 5  128  5
1 Jan 1970

Solution

 4

The reason this happens is because BroadcastChannel is partitioned by the site of the top-level document (through which your document is embedded). In the popup document it's not partitioned.

Although this is yet to be properly standardized, it's already implemented this way in most implementations.

2024-07-01
Anne