Skip to content

Commit

Permalink
Fix: add way to customize targetOrigin for calls to parent iframe (#18)
Browse files Browse the repository at this point in the history
* fix: allow setting parentTargetOrigin

* v0.3.0

* Update README.md
  • Loading branch information
enuchi authored Jun 1, 2021
1 parent d3d69ca commit dd525da
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 8 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,16 @@ Now we can use familiar Promises in our client-side code and have easy access to
## API

The config object takes:
`allowedDevelopmentDomains`: A config to specifiy which domains are permitted for communication with Google Apps Script Webpack Dev Server development tool. This is a security setting, and if not specified, will block functionality in development.

`allowedDevelopmentDomains` will accept either a space-separated string of allowed subdomains, e.g. `'https://localhost:3000 https://localhost:8080'` (notice no trailing slashes); or a function that takes in the requesting origin and should return `true` to allow communication, e.g. `(origin) => /localhost:\d+$/.test(origin);`
- `allowedDevelopmentDomains`: A config to specifiy which domains are permitted for communication with Google Apps Script Webpack Dev Server development tool. This is a security setting, and if not specified, will block functionality in development. `allowedDevelopmentDomains` will accept either a space-separated string of allowed subdomains, e.g. `'https://localhost:3000 https://localhost:8080'` (notice no trailing slashes); or a function that takes in the requesting origin and should return `true` to allow communication, e.g. `(origin) => /localhost:\d+$/.test(origin);`
- `parentTargetOrigin` An optional string to specify which parent window domain this client can send communication to. Defaults to own domain for backward compatibility with Google Apps Script Webpack Dev Server development tool (default uses domain where the client is running, e.g. localhost). Can be '*' to allow all parent domains if parent is unknown or variable.

### Production mode

In the normal Google Apps Script production environment, `new Server()` will have one available method:

- `serverFunctions`: an object containing all publicly exposed server functions (see example above).

Note that the `allowedDevelopmentDomains` configuration will be ignored in production, so the same code can and should be used for development and production.
Note that `allowedDevelopmentDomains` and `parentTargetOrigin` configurations will be ignored in production, so the same code can and should be used for development and production.

### Development mode

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gas-client",
"version": "0.2.1",
"version": "0.3.0",
"description": "A client-side utility class that can call server-side Google Apps Script functions",
"main": "build/index.js",
"files": [
Expand Down
10 changes: 7 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export default class Server {
/**
* Accepts a single `config` object
* @param {object} [config] An optional config object for use in development.
* @param {string|function} [config.allowedDevelopmentDomains] An optional config to specify which domains are permitted for communication with Google Apps Script Webpack Dev Server development tool. This is a security setting, and if not specified, this will block functionality in development. Will accept either a space-separated string of allowed subdomains, e.g. `https://localhost:3000 http://localhost:3000` (notice no trailing slash); or a function that takes in the requesting origin should return `true` to allow communication, e.g. `(origin) => /localhost:\d+$/.test(origin)`
* @param {string|function} [config.allowedDevelopmentDomains] An optional config to specify which domains are permitted for receiving communication from a parent window. This is a security setting, and if not specified, will block functionality in development. Will accept either a space-separated string of allowed subdomains, e.g. `https://localhost:3000 http://localhost:3000` (notice no trailing slash); or a function that takes in the requesting origin should return `true` to allow communication, e.g. `(origin) => /localhost:\d+$/.test(origin)`
* @param {string} [config.parentTargetOrigin] An optional config to specify which parent window domain this client can send communication to. Defaults to own domain for backward compatibility with Google Apps Script Webpack Dev Server development tool (domain where the client is running, e.g. localhost). Can be '*' to allow all parent domains.
*/
constructor(config = {}) {
// skip the reserved names: https://developers.google.com/apps-script/guides/html/reference/run
Expand Down Expand Up @@ -56,6 +57,10 @@ export default class Server {
// we'll store and access the resolve/reject functions here by id
window.gasStore = {};

// this domain should be restricted to googleusercontent.com but the subdomain is variable
// supports window.location.origin as default for backward compatibility
let targetOrigin = config.parentTargetOrigin || window.location.origin;

// set up the message 'receive' handler
const receiveMessageHandler = (event) => {
const { allowedDevelopmentDomains } = config;
Expand Down Expand Up @@ -101,8 +106,7 @@ export default class Server {
functionName,
args: [...args],
},
// only send messages to our dev server, which should be running on the same origin
window.location.origin
targetOrigin
);
return promise;
};
Expand Down
38 changes: 38 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,44 @@ describe('local development gas-client server', () => {
);
});

test('should default to post message to target origin of window.location.origin if no parentTargetOrigin is defined', () => {
const mockPostMessage = jest.fn();
window.parent.postMessage = mockPostMessage;
const defaultLocation = window.location.origin;

const server = new Server({});
server.serverFunctions.someFunction('arg1', 'arg2');

expect(mockPostMessage).toHaveBeenCalledWith(
expect.objectContaining({
args: ['arg1', 'arg2'],
functionName: 'someFunction',
id: expect.stringMatching(/^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/), // just simple check this is a uuid
type: 'REQUEST',
}),
defaultLocation
);
});

test("should set postMessage's target origin to parentTargetOrigin if defined", () => {
const mockPostMessage = jest.fn();
window.parent.postMessage = mockPostMessage;
const parentTargetOrigin = '*';

const server = new Server({ parentTargetOrigin });
server.serverFunctions.someFunction('arg1', 'arg2');

expect(mockPostMessage).toHaveBeenCalledWith(
expect.objectContaining({
args: ['arg1', 'arg2'],
functionName: 'someFunction',
id: expect.stringMatching(/^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/), // just simple check this is a uuid
type: 'REQUEST',
}),
parentTargetOrigin
);
});

test('should successfully handle received message and resolve successful server function response', () => {
const server = new Server({
allowedDevelopmentDomains: 'https://localhost:3000',
Expand Down

0 comments on commit dd525da

Please sign in to comment.