Skip to content
Merged
5 changes: 5 additions & 0 deletions .changeset/odd-pigs-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

Fixes an issue where iframe external commands sent via `window.postMessage` were not being handled correctly when Rocket.Chat was embedded inside an iframe.
1 change: 0 additions & 1 deletion apps/meteor/client/startup/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import './appRoot';
import './audit';
import './callbacks';
import './deviceManagement';
import './iframeCommands';
import './incomingMessages';
import './roles';
import './routes';
Expand Down
2 changes: 2 additions & 0 deletions apps/meteor/client/views/root/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { useDesktopTitle } from './hooks/useDesktopTitle';
import { useEmojiOne } from './hooks/useEmojiOne';
import { useEscapeKeyStroke } from './hooks/useEscapeKeyStroke';
import { useGoogleTagManager } from './hooks/useGoogleTagManager';
import { useIframeCommands } from './hooks/useIframeCommands';
import { useIframeLoginListener } from './hooks/useIframeLoginListener';
import { useLivechatEnterprise } from './hooks/useLivechatEnterprise';
import { useLoadMissedMessages } from './hooks/useLoadMissedMessages';
Expand Down Expand Up @@ -71,6 +72,7 @@ const AppLayout = () => {
useDesktopFavicon();
useDesktopTitle();
useStartupEvent();
useIframeCommands();

const layout = useSyncExternalStore(appLayout.subscribe, appLayout.getSnapshot);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import type { UserStatus, IUser } from '@rocket.chat/core-typings';
import { escapeRegExp } from '@rocket.chat/string-helpers';
import { afterLogoutCleanUpCallback } from '@rocket.chat/ui-client';
import type { LocationPathname } from '@rocket.chat/ui-contexts';
import { type LocationPathname, useSetting } from '@rocket.chat/ui-contexts';
import { Meteor } from 'meteor/meteor';
import { useEffect } from 'react';

import { AccountBox } from '../../app/ui-utils/client/lib/AccountBox';
import { sdk } from '../../app/utils/client/lib/SDKClient';
import { capitalize, ltrim, rtrim } from '../../lib/utils/stringUtils';
import { baseURI } from '../lib/baseURI';
import { loginServices } from '../lib/loginServices';
import { settings } from '../lib/settings';
import { getUser } from '../lib/user';
import { router } from '../providers/RouterProvider';
import { AccountBox } from '../../../../app/ui-utils/client/lib/AccountBox';
import { sdk } from '../../../../app/utils/client/lib/SDKClient';
import { capitalize, ltrim, rtrim } from '../../../../lib/utils/stringUtils';
import { baseURI } from '../../../lib/baseURI';
import { loginServices } from '../../../lib/loginServices';
import { settings } from '../../../lib/settings';
import { getUser } from '../../../lib/user';
import { router } from '../../../providers/RouterProvider';

const commands = {
'go'(data: { path: string }) {
Expand Down Expand Up @@ -93,27 +94,37 @@ type CommandMessage<TCommandName extends keyof typeof commands = keyof typeof co
externalCommand: TCommandName;
} & Parameters<(typeof commands)[TCommandName]>[0];

window.addEventListener('message', <TCommandMessage extends CommandMessage>(e: MessageEvent<TCommandMessage>) => {
if (!settings.peek<boolean>('Iframe_Integration_receive_enable')) {
return;
}
export const useIframeCommands = () => {
const iframeReceiveEnabled = useSetting('Iframe_Integration_receive_enable');
const iframeReceiveOrigin = useSetting('Iframe_Integration_receive_origin', '*');

if (typeof e.data !== 'object' || typeof e.data.externalCommand !== 'string') {
return;
}
useEffect(() => {
if (!iframeReceiveEnabled) {
return;
}
const messageListener = (event: MessageEvent<CommandMessage>) => {
if (typeof event.data !== 'object' || typeof event.data.externalCommand !== 'string') {
return;
}

const origins = settings.peek<string>('Iframe_Integration_receive_origin') ?? '*';
if (iframeReceiveOrigin !== '*' && iframeReceiveOrigin.split(',').indexOf(event.origin) === -1) {
console.error('Origin not allowed', event.origin);
return;
}

if (origins !== '*' && origins.split(',').indexOf(e.origin) === -1) {
console.error('Origin not allowed', e.origin);
return;
}
if (!(event.data.externalCommand in commands)) {
console.error('Command not allowed', event.data.externalCommand);
return;
}

if (!(e.data.externalCommand in commands)) {
console.error('Command not allowed', e.data.externalCommand);
return;
}
const command: (data: MessageEvent['data'], event: MessageEvent) => void = commands[event.data.externalCommand];
command(event.data, event);
};

window.addEventListener('message', messageListener);

const command: (data: any, event: MessageEvent) => void = commands[e.data.externalCommand];
command(e.data, e);
});
return () => {
window.removeEventListener('message', messageListener);
};
}, [iframeReceiveEnabled, iframeReceiveOrigin]);
};
Loading