Using JavaScript and window.postMessage() for Safe Cross-Domain Communication
Cross-Domain communication (also called Cross-origin) can be difficult and pose security risks.
As stated on MDN
The window.postMessage() method safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it.
So here is the senerio…
I want to communicate between two windows. I have two separate sites in domains abc[dot]com and xyz[dot]com respectively. There is a useful feature of window.postMessage(), which is safe if used correctly.
Usage:
Syntax
targetWindow.postMessage(message, targetOrigin);
If “*” is used as targetOrigin, the message could be from anyone. we can validate the receivers domain against the targetOrigin. If they do not match, do not accept the message.
Example
We will create two web pages named controller.html and receiver.html. receiver.html will be a pop-up(open in a new window). Now, receiver.html will send data to controller.html and controller.html will send a response to receiver.html.
controller.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script>
function openUrl() {
let inputVal = document.getElementById("myInput").value;
let targetWindow = window.open(inputVal);
let eventMethod = window.addEventListener
? "addEventListener"
: "attachEvent";
let eventer = window[eventMethod];
let messageEvent =
eventMethod === "attachEvent" ? "onmessage" : "message";
// Listen to message from child window
eventer(
messageEvent,
function (e) {
// Check if origin is proper
if (e.origin !== "http://127.0.0.1:3000") {
return;
}
const obj = e.data;
const tbody = document.getElementById("tbody");
for (var i = 0; i < obj.length; i++) {
var tr = "<tr>";
/* Verification to add the last decimal 0 */
if (
obj[i].value
.toString().substring(obj[i].value.toString().indexOf("."),obj[i].value.toString().length) < 2)
obj[i].value += "0";
/* Must not forget the $ sign */
tr += "<td>" +obj[i].key + "</td>" + "<td>$" + obj[i].value.toString() + "</td></tr>";
/* We add the table row to the table body */
tbody.innerHTML += tr;
}
// Here we are sending back response to receiver.html
var pathArray = inputVal?.split("/");
var url =
pathArray && pathArray[0] + "//" + pathArray[2];
targetWindow.postMessage( { access: true }, "http://127.0.0.1:3000/");
},
false
);
}
</script>
</head>
<body>
<form>
<fieldset>
<input
type="text"
placeholder="Type something..."
id="myInput"
/>
<button type="button" onclick="openUrl();">open URL</button>
</fieldset>
</form>
<table>
<tbody id="tbody"></tbody>
</table>
</body>
</html>
receiver.html
Recommended by LinkedIn
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Popup</title>
<script type="text/javascript">
window.opener.postMessage(
[
{
key: "apple",
value: 1.9,
},
{
key: "berry",
value: 1.7,
},
{
key: "banana",
value: 1.5,
},
{
key: "cherry",
value: 1.2,
},
],
"http://127.0.0.1:3000/"
);
window.addEventListener(
"message",
function (e) {
// Check if origin is proper
if (e.origin != "http://127.0.0.1:3000") {
return;
}
document.getElementById(
"demo"
).innerHTML = `Access: ${e?.data?.access}`;
},
false
);
</script>
</head>
<body>
<p id="demo"></p>
</body>
</html>
Test Run
Open controller.html and enter the URL of receiver.html. Now the receiver.html will popup and it will send data to the controller.html
Once controller.html gets the data it will send back response to receiver.html
What is happening?
When we entered the URL of receiver.html, an object has been sent back to the controller.html from receiver.html using the reference(window.opener.postMessage()). Again the controller sent a response to the receiver.html using the reference of the popup window(targetWindow.postMessage()).
So, window.postMessage() is a safe way to send messages between windows in different domains or origins.
Useful Links
I write code - For work; for fun.
2yAe bey ki suka bey