A powerful, simple, promise-based, encrypted postMessage
library.
EncryptedPostmate is a promise-based API built on postMessage
. It allows a parent page to speak with a child iFrame
across origins with minimal effort.
You can download the compiled javascript directly here
Features
- Promise-based API for elegant and simple communication.
- Secure two-way parent <-> child handshake, with message validation.
- Child exposes a retrievable
model
object that the parent can access. - Child emits events that the parent can listen to.
- Parent can
call
functions within a child
- Zero dependencies. Provide your own polyfill or abstraction for the
Promise
API if needed. - Lightweight, weighing in at ~
5.3kb
.
Installing
EncryptedPostmate can be installed via NPM or Bower.
NPM
$ yarn add encrypted-postmate
$ npm i encrypted-postmate --save
Glossary
Parent
: The top level page that will embed an iFrame
, creating a Child
.Child
: The bottom level page loaded within the iFrame
.Model
: The object that the Child
exposes to the Parent
.Handshake
: The process by which the parent frame identifies itself to the child, and vice versa. When a handshake is complete, the two contexts have bound their event listeners and identified one another.
Usage
-
The Parent
begins communication with the Child
. A handshake is sent, the Child
responds with a handshake reply, finishing Parent
/Child
initialization. The two are bound and ready to communicate securely.
-
The Parent
fetches values from the Child
by property name. The Child
can emit messages to the parent. The Parent
can call
functions in the Child
Model
.
Example
parent.com
const handshake = new EncryptedPostmate({
container: document.getElementById('some-div'),
url: 'http://child.com/page.html' postmate.spec.jstion.
});
handshake.then(child => {
child.get('height')
.then(height => child.frame.style.height = `${height}px`);
child.on('some-event', data => console.log(data));
});
child.com/page.html
const handshake = new EncryptedPostmate.Model({
height: () => document.height || document.body.offsetHeight
});
handshake.then(parent => {
parent.emit('some-event', 'Hello, World!');
});
API
Postmate.debug
EncryptedPostmate.debug = true;
new EncryptedPostmate(options);
Name | Type | Description | Default |
---|
debug | Boolean | Set to true to enable logging of additional information | false |
Postmate(options)
new EncryptedPostmate({
container: document.body,
url: 'http://child.com/',
model: { foo: 'bar' }
});
This is written in the parent page. Initiates a connection with the child. Returns a Promise that signals when the handshake is complete and communication is ready to begin.
Returns: Promise(child)
Properties
Name | Type | Description | Default |
---|
container (optional) | DOM Node Element | An element to append the iFrame to | document.body |
url | String | _A URL to load in the iFrame. The origin of this URL will also be used for securing message transport | none_ |
model | Object | An object literal to represent the default values of the Childs model | none |
Postmate.Model(model)
// child.com
new EncryptedPostmate.Model({
// Serializable values
foo: "bar",
// Functions
height: () => document.height || document.body.offsetHeight,
// Promises
data: fetch(new Request('data.json'))
});
> This is written in the child page. Calling `Postmate.Model` initiates a handshake request listener from the `Parent`. Once the handshake is complete, an event listener is bound to receive requests from the `Parent`. The `Child` model is _extended_ from the `model` provided by the `Parent`.
#### Parameters
Name | Type | Description | Default
:--- | :--- | :--- | :---
**`model`** | `Object` | _An object of gettable properties to expose to the parent. Value types may be anything accepted in `postMessage`. Promises may also be set as values or returned from functions._ | `{}`
***
> ## `child.get(key)`
```javascript
// parent.com
new EncryptedPostmate({
container: document.body,
url: 'http://child.com/'
}).then(child => {
child.get('something').then(value => console.log(value));
});
Retrieves a value by property name from the Childs
model
object.
Returns: Promise(value)
Parameters
Name | Type | Description |
---|
key | String (required) | The string property to lookup in the childs model |
child.call(key, data)
new EncryptedPostmate({
container: document.body,
url: 'http://child.com/'
}).then(child => {
child.call('sayHi', 'Hello, World!');
});
Calls the function sayHi
in the Child
Model
with the parameter Hello, World!
Returns: undefined
Parameters
Name | Type | Description |
---|
key | String (required) | The string property to lookup in the childs model |
data | Mixed | The optional data to send to the child function |
child.destroy()
new EncryptedPostmate({
container: document.body,
url: 'http://child.com/'
}).then(child => child.destroy());
Removes the iFrame
element and destroys any message
event listeners
Returns: undefined
##child.frame
new EncryptedPostmate(options).then(child => {
child.get('height')
.then(height => child.frame.style.height = `${height}px`);
});
The iFrame Element that the parent is communicating with
Troubleshooting/FAQ
General
Why use Promises for an evented API?
Promises provide a clear API for fetching data. Using an evented approach often starts backwards. if the parent wants to know the childs height, the child would need to alert the parent, whereas with EncryptedPostmate, the Parent will request that information from the child in a synchronous-like manner. The child can emit events to the parent as well, for those other use-cases that still need to be handled.
Silent Parent/Child
I've enabled logging but the parent or child is not logging everything.
EncryptedPostmate.debug needs to be set in both the parent and child for each of them to log their respective information
The child does not respond to communication from the Parent
Make sure that you have initialized EncryptedPostmate.Model in your child page.
Restrictive Communication
I want to retrieve information from the parent by the child
EncryptedPostmate (by design) is restrictive in its modes of communication. This enforces a simplistic approach: The parent is responsible for logic contained within the parent, and the child is responsible for logic contained within the child. If you need to retrieve information from parent -> child, consider setting a default model
in the parent that the child may extend.
I want to send messages to the child from the parent
This is specifically what the call
function is for.
Security
What is the Handshake and why do I need one?
By default, all message
events received by any (parent) page can come from any (child) location. This means that the Parent
must always enforce security within its message event, ensuring that the child
(origin) is who we expect them to be, that the message is a response from an original request, and that our message is valid. The handshake routine solves this by saving the identities of the child and parent and ensuring that no changes are made to either.
How are messages validated?
The origin of the request, the message type, the postMessage mime-type, and in some cases the message response, are all verified against the original data made when the handshake was completed.
License
MIT