Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

With contextIsolation = true, is it possible to use ipcRenderer?

Here's my setup:

Step 1. Create a preload.js file with the code:

window.ipcRenderer = require('electron').ipcRenderer;

Step 2. Preload this file in your main.js via webPreferences:

  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });

Step 3. In a renderer:

console.log(window.ipcRenderer); // Works!

Now following Electron's security guide, I wish to turn contextIsolation=true: https://electronjs.org/docs/tutorial/security#3-enable-context-isolation-for-remote-content

Step 2bis.

  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      contextIsolation: true,
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });

Step 3bis. In a renderer:

console.log(window.ipcRenderer); // undefined

Question: can I use ipcRenderer when contextIsolation=true?

like image 611
jeanpaul62 Avatar asked Mar 14 '19 13:03

jeanpaul62


People also ask

What is electron Contextisolation?

​ Context Isolation is a feature that ensures that both your preload scripts and Electron's internal logic run in a separate context to the website you load in a webContents .

What is contextBridge in electron?

Create a safe, bi-directional, synchronous bridge across isolated contexts. Process: Renderer. An example of exposing an API to a renderer from an isolated preload script is given below: // Preload (Isolated World)

What is electron preload?

Summary​ A preload script contains code that runs before your web page is loaded into the browser window. It has access to both DOM APIs and Node. js environment, and is often used to expose privileged APIs to the renderer via the contextBridge API.


2 Answers

new answer

You can follow the setup outlined here. This setup is being used in secure-electron-template Essentially, this is what you can do:

main.js

const {
  app,
  BrowserWindow,
  ipcMain
} = require("electron");
const path = require("path");
const fs = require("fs");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;

async function createWindow() {

  // Create the browser window.
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false, // is default value after Electron v5
      contextIsolation: true, // protect against prototype pollution
      enableRemoteModule: false, // turn off remote
      preload: path.join(__dirname, "preload.js") // use a preload script
    }
  });

  // Load app
  win.loadFile(path.join(__dirname, "dist/index.html"));

  // rest of code..
}

app.on("ready", createWindow);

ipcMain.on("toMain", (event, args) => {
  fs.readFile("path/to/file", (error, data) => {
    // Do something with file contents

    // Send result back to renderer process
    win.webContents.send("fromMain", responseObj);
  });
});

preload.js

const {
    contextBridge,
    ipcRenderer
} = require("electron");

// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld(
    "api", {
        send: (channel, data) => {
            // whitelist channels
            let validChannels = ["toMain"];
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, data);
            }
        },
        receive: (channel, func) => {
            let validChannels = ["fromMain"];
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender` 
                ipcRenderer.on(channel, (event, ...args) => func(...args));
            }
        }
    }
);

index.html

<!doctype html>
<html lang="en-US">
<head>
    <meta charset="utf-8"/>
    <title>Title</title>
</head>
<body>
    <script>
        window.api.receive("fromMain", (data) => {
            console.log(`Received ${data} from main process`);
        });
        window.api.send("toMain", "some data");
    </script>
</body>
</html>

original

You are still able to use ipcRenderer in a renderer process with contextIsolation set to true. The contextBridge is what you want to use, although there is a current bug that is preventing from you calling ipcRenderer.on in a renderer process; all you can do is send from the renderer process to the main process.

This code is taken from secure-electron-template a template for Electron built with security in mind. (I am the author)

preload.js

const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld(
    "electron",
    {
        ipcRenderer: ipcRenderer
    }
);

main.js

let win;

async function createWindow() {

  // Create the browser window.
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      nodeIntegrationInWorker: false,
      nodeIntegrationInSubFrames: false,
      contextIsolation: true,
      enableRemoteModule: false,
      preload: path.join(__dirname, "preload.js")
    }
  });
}

some renderer.js file

window.electron.ipcRenderer

like image 82
reZach Avatar answered Sep 21 '22 07:09

reZach


Notice this sentence in the middle of the description of context isolation. It's easy to miss.

The Electron API will only be available in the preload script and not the loaded page.

Looks like the answer is no.

like image 28
pushkin Avatar answered Sep 18 '22 07:09

pushkin