LoginSignup
153

More than 5 years have passed since last update.

posted at

updated at

ElectronでのOAuth

Electronからtwitter APIを叩こうとして少しハマったのでメモ.
ElectronにおけるOAuthのログイン処理関連の話なので, 別にTwitterだけに限った話ではないはず。

ハマった内容

遭遇した問題としては, ElectronからTwitterのAPIを叩こうとしたのだけど, oauth_verifierが上手く取得できない, という内容だ.

最初, oauthのcallbackに 'file://' + __dirname + 'redirect.html'のようにファイルスキーマのURLを指定してみたのだけど, 下記のようにエラーが表示されてしまった.

Not allowed to load local resource: file:///(中略)/redirect.html?oauth_token=...&oauth_verifier=...

ログから, oauth_verifierの取得自体は成功しているもの, 画面遷移でコケているのは明らかだ.
通常, Electronのアプリでは, rendererProcess中でlocation.hrefを参照するとファイルスキーマURLが返ってくるので, 動くのかなと思ったけど, どうもダメな様子.

issue#483を見ると, 「BrowserWindow作成時に, {'web-preferences': {'web-security': false}}にすれば出来るよ」みたいな記載もあったので試してみたけど, 特に改善されず.
2016.03.21追記
{webPreferences: {webSecurity: false}} とすることで, 上記のエラーを抑止し、ファイルスキーマURLへリダイレクト可能となるという情報をコメントにて頂きました.

解決策

最終的に, 下記のようにすることでトークンの取得が出来るようになった.

main.js
const BrowserWindow = require('app').BrowserWindow;
const twitterAPI = require('node-twitter-api');

const twitter = new twitterAPI({
  consumerKey: /* コンシューマキー */,
  consumerSecret: /* コンシューマシークレット */,
  callback: 'https://www.google.co.jp/' // 別にどこでもよい
});

twitter.getRequestToken((error, requestToken, requestTokenSecret) => {
  const url = twitter.getAuthUrl(requestToken);
  const loginWindow = new BrowserWindow({width: 800, height: 600});
  loginWindow.webContents.on('will-navigate', (event, url) => {
    // https://www.google.co.jp/?oauth_token=...&oauth_verifier=...のようなURLが渡ってくる.
    var matched;
    if(matched = url.match(/\?oauth_token=([^&]*)&oauth_verifier=([^&]*)/)) {
      twitter.getAccessToken(requestToken, requestTokenSecret, matched[2], (error, accessToken, accessTokenSecret) => {
        console.log('accessToken', accessToken);
        console.log('accessTokenSecret', accessTokenSecret);
      });
    }
    event.preventDefault();
    loginWindow.close();
  });
  loginWindow.loadURL(url);
});

ポイントは下記の2点:

  • コールバックのURLは, http:// or https://から始まっていればどこでもよい.
  • BrowserWindow.WebContentsのwill-navigateイベントで画面遷移(URL変更)のイベントをフックして, クエリストリングからトークンが取得できる

そもそも方向性が合っているのか?(2016.08.25 追記)

このエントリを上げてから1年以上経って、今更の振り返りではあるものの、Electron + OAuthでユーザの認証画面を表示するのにBrowserWindowを利用する事自体、筋が良くないと思うようになった。

その理由は至極単純で、Twitterのパスワードを入力する画面を、アプリケーション(Electron)側に持つとユーザーに「このアプリが悪意を持っていたとしたら、自分のパスワードが抜かれてしまうのでは」という危惧を与えることに繋がるからだ。Twitterで言うのであれば、PIN-based OAuthを利用した方が良い。

const { OAuth } = require("oauth");
const { app, BrowserWindow, shell, ipcMain} = require("electron");

let win;
const schema = "electron";
app.on("ready", () => {
  win = new BrowserWindow();
  const oauth = new OAuth(
    "https://api.twitter.com/oauth/request_token",
    "https://api.twitter.com/oauth/access_token",
    "your consumer key",
    "your consumer secret",
    "1.0A",
    null,
    "HMAC-SHA1"
  );

  oauth.getOAuthRequestToken((error, oauthToken, oauthTokenSecret, results) => {
    if (error) return;
    const authUrl = `https://api.twitter.com/oauth/authorize?oauth_token=${oauthToken}`;
    // デフォルトブラウザに認証画面を表示
    shell.openExternal(authUrl);

    // PINコード入力画面を表示
    win.loadURL(`file://${__dirname}/pinBased.html`);

    // 入力したPINコードはIPC通信でRenderer -> Main に受け渡す
    ipcMain.once("SEND_PIN", (e, args) => {
      const oauthVerifier = args.pin;
      console.log(oauthToken, oauthVerifier);
      oauth.getOAuthAccessToken(oauthToken, oauthTokenSecret, oauthVerifier, (error, accessToken, accessTokenSecret) => {
        console.log('accessToken', accessToken);
        console.log('accessTokenSecret', accessTokenSecret);
      });
    });
  });

});

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
153