Using JavaScript and window.postMessage()

Using JavaScript and window.postMessage()

Safe Cross-Domain Communication

Image for postPhoto by Kate Macate on Unsplash

Introduction

Cross-Domain communication (also called Cross-origin) can be difficult and pose security risks. However, there is a useful and often overlooked feature of HTML5, window.postMessage(), which is safe if used correctly.

As stated on the 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.

In this article, we will focus on communication between windows and not a window and an iframe.

By the way, the statement on the MDN says pop-up window, which we will use, but the recipient window does not have to be a pop-up.

Syntax

The two required components and their syntax

The two required components are,

  1. window.postMessage() ? to send the message
  2. window.addEventListener(?message?,callback) ? to receive and process the message

The syntax for the postMessage() method is,

targetWindow.postMessage(message, targetOrigin, [transfer]);

There is a optional third parameter, [transfer], that we will not be using. You may want to read more about on the MDN.

  • targetWindow is a handle to the window to which you want to send the message.
  • message can be quite a few complex objects. However, functions cannot be sent as part of the message as the message data is serialized using the structured clone algorithm. The structured clone algorithm does not allow for functions. However, this does mean you can pass a broad variety of data Objects safely.
  • targetOrigin is a very important piece. It is the URI of the recipient page. At the moment of dispatch (postMessage), if the targetOrigin, does match the host name of targetWindow?s page, it will fail to send. It is possible to use ?*? as the targetorigin, but only do that for simple testing (as we will do here.)

To stress this further, in production, on the receiving end, you will want to validate the receivers domain against the targetOrigin. If they do not match, do not accept the message.

Beware: If ?*? is used as targetOrigin, the message could be from anyone.

Keep in mind, we are typically sending from one domain to another, so targetOrigin must match the domain of the targetWindow listener.

In other words, we may want to send a message from https://abcd.com to https://defg.com. So targetOrigin would be https://defg.com. and the recipient?s domain would be https://defg.com as well.

Example

We will create two web pages named Page1.html and Page2.html. Page2.html will be a pop-up, it does not have to be, this is just my choice.

Page1.html will send a message to Page2.html.

Pages

Create the following files

Page1.html (note the sendMessage function)

<!DOCTYPE html><html><head> <title></title> <meta charset=”utf-8″ /><script>var childwin;const childname = “popup”;function openChild() {childwin = window.open(‘Page2.html’, childname, ‘height=300px, width=500px’); }function sendMessage(){ let msg={pName : “Bob”, pAge: “35”}; // In production, DO NOT use ‘*’, use toe target domain childwin.postMessage(msg,’*’)// childwin is the targetWindow childwin.focus();}</script></head><body> <form> <fieldset> <input type=’button’ id=’btnopen’ value=’Open child’ onclick=’openChild();’ /> <input type=’button’ id=’btnSendMsg’ value=’Send Message’ onclick=’sendMessage();’ /> </fieldset> </form></body></html>

Page2.html (note callback function in addEventListener)

<!DOCTYPE html><html><head> <title></title> <meta charset=”utf-8″ /> <script>// Allow window to listen for a postMessage window.addEventListener(“message”, (event)=>{// Normally you would check event.origin // To verify the targetOrigin matches // this window’s domain let txt=document.querySelector(‘#txtMsg’); // event.data contains the message sent txt.value=`Name is ${event.data.pName} Age is ${event.data.pAge}` ; });function closeMe() { try {window.close(); } catch (e) { console.log(e) } try {self.close(); } catch (e) { console.log(e) }} </script></head><body> <form> <h2>Recipient of postMessage</h2> <fieldset> <input type=’text’ id=’txtMsg’ /> <input type=’button’ id=’btnCloseMe’ value=’Close me’ onclick=’closeMe();’ /> </fieldset> </form></body></html>

Test Run

Open Page1.html and click the ?Open child? button. This opens the pop-up.

Image for postPopup-targetWindow

Click the ?Send Message? button. The message is received by the pop-up window.

Image for postResult of childwin.postMessage()

What is Happening?

When ?Send Message? is clicked, an Object is being sent to the recipient page using the reference to that window, childwin, created during window.open().

childwind.postMessage(msg,?*?)

Again, DO NOT use ?*? other than for testing.

The targetOrigin should be the domain in which the recipient page resides.

The Event Listener of the recipient window receives the message in a parameter named ?event?.

The callback function, an Arrow function in our case, processes the message.

  • The message is contained in event.data.
  • The targetOrigin is contained in event.origin.

Conclusion

window.postMessage() is a safe way to send messages between windows in different domains or origins. One can also post to an IFrame.

The data being sent is serialized using the structured clone algorithm and will accept almost any type of simple or complex data.

There is also a postMessage() that can be used when the browser context is the same. This involves a channel.

channel.postMessage() will be covered in another article.

Thank you for reading and happy coding!

You may also enjoy,

Using JavaScript and The Broadcast Channel API

For Browser Context Communication Between Tabs

medium.com

Introduction to JavaScript?s Geolocation API

What JavaScript?s Geolocation API is and how to use it

medium.com

Closing a Window with JavaScript

What works, what doesn?t, in a nutshell

medium.com

24