Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

ElectronでProxy認証 - ユーザー入力ウィンドウ編

More than 1 year has passed since last update.

はじめに

Electronアプリで社内NWから外部へfetchした時、プロキシ通過でちょっと詰まったのでメモ

結論

loginイベント + IPC(プロセス間通信)で解決しました。

具体的な流れとしては、以下のようになります。
1. ログイン認証要求
2. メインプロセスでloginイベントをキャッチ
3. 入力ウィンドウを表示
4. 入力されたユーザー情報をIPCでメインプロセスに送信
5. メインプロセスで受信したユーザー情報をサーバーに転送

解説

loginイベント

メインプロセス
const app = electron.app;

app.on("login", (event, webContents, request, authInfo, callback)=>{
    if(authInfo.isProxy){
        event.preventDefault();
        // ほげほげ
        callback(username, password);
    }
});

基本的に他のイベント処理と同じです。
登録しておくと、ログイン要求があった際にキャッチし、認証等の処理が行えるようになります。

event.preventDefault();が、認証をスキップせず保留してくれるメソッドです。
プロキシの場合はauthInfo.isProxyにて判別可能で、ユーザー情報はcallback(username, password)で送信できます。

IPC(プロセス間通信)

メインプロセス
const ipcMain = electron.ipcMain;

ipcMain.on("channel-name", (event, arg)=>{
    // ほげほげ
});
レンダラープロセス
const ipcRenderer = electron.ipcRenderer;

ipcRenderer.send("channel-name", arg);

IPCとは、プロセス間でデータ伝達を行う為の仕組みです。

ElectronでのIPCは、メインプロセス/レンダラープロセス間で相互にデータ伝達する際に使います。
チャネルと言う概念があり、送信時に指定したチャネル名と、受信待機時に指定したチャネル名が紐付く事で、通信が可能になります。

メインプロセス/レンダラープロセスで、使用するメンバ名が若干違いますが、機能的には大体同じです。

ipc.on()で受信待機、ipc.send()で送信、argが受け渡しデータ本体となります。

これは非同期通信の例ですが、同期通信する方法もあるようです。
が、出番は少ないとの事なので割愛します。

構築

メインプロセス側

メインウィンドウの記述は端折ってます。

index.js
const electron = require("electron");
const app = electron.app;

// メインプロセス側のIPCメンバ
const ipcMain = electron.ipcMain;

let loginWindow;
let isLoginWindow = false;

// ログイン要求時に発火するイベント
app.on("login", (event, webContents, request, authInfo, callback)=>{

    // プロキシサーバーからの要求だったら続行
    if(authInfo.isProxy){

        // 重複発火対策
        if(!isLoginWindow){
            isLoginWindow = true;
        }
        else{
            return;
        }

        // 認証情報が送信されるまで待機
        event.preventDefault();

        loginWindow = new electron.BrowserWindow({
            width: 210,
            height: 140,
            resizable: false
        });

        // 入力ウィンドウをロード
        loginWindow.setMenu(null);
        loginWindow.loadURL('file://' + __dirname + '/login.html');

        // IPCチャネル"proxy-auth"で受信待機
        ipcMain.on("proxy-auth", (event, username, password)=>{

            // 受信した認証情報をプロキシサーバーへ転送
            callback(username, password);
            isLoginWindow = false;
        });
    }
});

途中、2連続でloginイベントが発火する、原因不明の現象に見舞われました。
1回目の発火でフラグを立てて、2回目以降はエスケープする処理を入れました。

レンダラープロセス側

認証情報(ユーザー名/パスワード)の入力ページを組み立てます。

login.html
<body>
    <input type="text" id="username">
    <input type="password" id="password">
    <button onclick="sendIpc();">ログイン</button>
</body>

HTMLはごく普通の入力フォームです。

const electron = require("electron");

// レンダラープロセス側のIPCメンバ
const ipcRenderer = electron.ipcRenderer;

// レンダラープロセス側からメインプロセス側を操作するメンバ
const remote = electron.remote;

// ログインボタン
function sendIpc(){

    // 入力ウィンドウをカレントにする
    const currentWindow = remote.getCurrentWindow();

    // IPCチャネル"proxy-auth"でユーザー名/パスワードを送信
    ipcRenderer.send(
        "proxy-auth",
        document.getElementById("username").value,
        document.getElementById("password").value
    );

    // 入力ウィンドウを自動的に閉じる
    currentWindow.close();
}

これで基本的な実装は完成しました。

まとめ

プロセス間通信って、名前聞いただけで凄く難しそうなイメージでしたが、何の事はありませんでした。

イメージしてた、VisualStudio Codeのプロキシ認証画面のような感じに出来たので、満足です。

(IPCでパスワードのやり取りが大丈夫なのかは、組んだ後にちょっと気になった)

dojyorin
独学でJavaScriptとC++やってます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away