diff --git a/src/components/confirmDialog/confirmDialog.component.ts b/src/components/confirmDialog/confirmDialog.component.ts
index c27500612afb8eec97047344c7662343cf569c75..b1782761449051a601e8fac5046241e92c572433 100644
--- a/src/components/confirmDialog/confirmDialog.component.ts
+++ b/src/components/confirmDialog/confirmDialog.component.ts
@@ -16,9 +16,17 @@ export class ConfirmDialogComponent {
   @Input()
   public message: string = 'Would you like to proceed?'
 
+  @Input()
+  public okBtnText: string = `OK`
+
+  @Input()
+  public cancelBtnText: string = `Cancel`
+
   constructor(@Inject(MAT_DIALOG_DATA) data: any) {
-    const { title = null, message  = null} = data || {}
-    if (title) { this.title = title }
-    if (message) { this.message = message }
+    const { title = null, message  = null, okBtnText, cancelBtnText} = data || {}
+    if (title) this.title = title
+    if (message) this.message = message
+    if (okBtnText) this.okBtnText = okBtnText
+    if (cancelBtnText) this.cancelBtnText = cancelBtnText
   }
 }
diff --git a/src/components/confirmDialog/confirmDialog.template.html b/src/components/confirmDialog/confirmDialog.template.html
index eb0c76fffdca11eda3487eb259b2be3cbbaa032e..df52270e96592f165d5497b5ecb610cfc92bcbcf 100644
--- a/src/components/confirmDialog/confirmDialog.template.html
+++ b/src/components/confirmDialog/confirmDialog.template.html
@@ -11,6 +11,6 @@
 <mat-divider></mat-divider>
 
 <mat-dialog-actions class="justify-content-start flex-row-reverse">
-    <button [mat-dialog-close]="true" mat-raised-button color="primary">OK</button>
-  <button [mat-dialog-close]="false" mat-button>Cancel</button>
-</mat-dialog-actions>
\ No newline at end of file
+    <button [mat-dialog-close]="true" mat-raised-button color="primary">{{ okBtnText }}</button>
+  <button [mat-dialog-close]="false" mat-button>{{ cancelBtnText }}</button>
+</mat-dialog-actions>
diff --git a/src/main.module.ts b/src/main.module.ts
index 7dbd23fa58c4521f5cfd915bbfe618500027fa86..3cc29399b3f96f5f9a7784197c66c8f62e436886 100644
--- a/src/main.module.ts
+++ b/src/main.module.ts
@@ -57,6 +57,7 @@ import { DatasetPreviewGlue, datasetPreviewMetaReducer, IDatasetPreviewGlue, Glu
 import { viewerStateHelperReducer, viewerStateMetaReducers, ViewerStateHelperEffect } from './services/state/viewerState.store.helper';
 import { TOS_OBS_INJECTION_TOKEN } from './ui/kgtos/kgtos.component';
 import { UiEffects } from './services/state/uiState/ui.effects';
+import { MesssagingModule } from './messaging/module';
 
 export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
   return function(state, action) {
@@ -83,6 +84,7 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
     PluginModule,
     LoggingModule,
     ShareModule,
+    MesssagingModule,
 
     SpotLightModule,
     
diff --git a/src/messaging/module.ts b/src/messaging/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..191d3e959c3a2b849eaa15ae04d791f0545f31cb
--- /dev/null
+++ b/src/messaging/module.ts
@@ -0,0 +1,98 @@
+import { NgModule } from "@angular/core";
+import { MatDialog } from "@angular/material/dialog";
+import { ComponentsModule } from "src/components";
+import { ConfirmDialogComponent } from "src/components/confirmDialog/confirmDialog.component";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+
+const IAV_POSTMESSAGE_NAMESPACE = `ebrains:iav:`
+
+@NgModule({
+  imports: [
+    AngularMaterialModule,
+    ComponentsModule,
+  ]
+})
+
+export class MesssagingModule{
+
+  private whiteListedOrigins = new Set()
+  private pendingRequests: Map<string, Promise<boolean>> = new Map()
+
+  constructor(
+    private dialog: MatDialog
+  ){
+
+    window.addEventListener('message', async ({ data, origin, source }) => {
+      const { method, id } = data
+      const src = source as Window
+      if (!method) return
+      if (method.indexOf(IAV_POSTMESSAGE_NAMESPACE) !== 0) return
+      const strippedMethod = method.replace(IAV_POSTMESSAGE_NAMESPACE, '')
+      switch (strippedMethod) {
+      case 'ping': {
+        window.opener.postMessage({
+          id,
+          result: 'pong',
+          jsonrpc: '2.0'
+        }, origin)
+
+        break
+      }
+      case 'dummyMethod': {
+        try {
+          const result = await this.dummyMethod({ data, origin })
+          src.postMessage({
+            id,
+            result
+          }, origin)
+        } catch (e) {
+
+          src.postMessage({
+            id,
+            error: e.code
+              ? e
+              : { code: 500, message: e.toString() }
+          }, origin)
+        }
+            
+        break;
+      }
+      default: {
+        src.postMessage({
+          id,
+          error: {
+            code: 404,
+            message: 'Method not found'
+          }
+        }, origin)
+      }
+      }
+    })
+  }
+
+  async checkOrigin({ origin }){
+    if (this.whiteListedOrigins.has(origin)) return true
+    if (this.pendingRequests.has(origin)) return this.pendingRequests.get(origin)
+    const responsePromise = this.dialog.open(
+      ConfirmDialogComponent,
+      {
+        data: {
+          title: `Cross tab messaging`,
+          message: `${origin} would like to send data to interactive atlas viewer`,
+          okBtnText: `Allow`
+        }
+      }
+    ).afterClosed().toPromise()
+    this.pendingRequests.set(origin, responsePromise)
+    const response = await responsePromise
+    this.pendingRequests.delete(origin)
+    if (response) this.whiteListedOrigins.add(origin)
+    return response
+  }
+
+  async dummyMethod({ data, origin }){
+    const allow = await this.checkOrigin({ origin })
+    if (!allow) throw ({ code: 403, message: 'User declined' })
+    return 'OK'
+  }
+}
\ No newline at end of file