angular.js+chrome_ex_oauth

2013-11-18T00:00:00+00:00 angular.js Chrome Extension JavaScript

angular.jsを使いつつ、chrome_ex_oauthを使ってTwitter APIを使うデモとか出来るんじゃないかなーって思ってやってみた

manifest.json

{
  "name": "sample",
  "version": "0.1",
  "manifest_version": 2,
  "background": {
    "scripts": [
      "js/angular.js",
      "js/chrome_ex_oauth.js",
      "js/chrome_ex_oauthsimple.js",
      "const.js",
      "background.js"
    ]
  },
  "content_scripts": [
    {
      "matches": ["https://api.twitter.com/oauth/authorize"],
      "js": ["content_script.js"],
      "run_at": "document_idle"
    }
  ],
  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "popup.html"
  },
  "permissions": ["https://api.twitter.com/*"],
  "content_security_policy": "script-src 'self' 'unsafe-eval';object-src 'self'"
}

Twitter APIだとoauth_callback?でchrome-extension://な方式が使えない以前に、通常のアプリケーション方式だとリクエストトークンを取得する時点でoauth_callbackパラメーターが許容されない為、authorizeからのアクセストークンを取得する流れをコントロールする必要がある。そこはコンテントスクリプトを使ってアクセストークンを取得して保管する

popup.html

<html ng-app>
  <head>
    <script src="js/angular.js"></script>
    <script src="js/chrome_ex_oauth.js"></script>
    <script src="js/chrome_ex_oauthsimple.js"></script>
    <script src="const.js"></script>
    <script src="popup.js"></script>
    <link rel="stylesheet" type="text/css" href="popup.css" />
  </head>
  <body>
    <div ng-controller="TwitterController">
      <div ng-repeat="tweet in tweets" class="tweet">
        <div class="tweet-icon">
          <img ng-src="{% raw %}{{tweet.user.profile_image_url_https}}{% endraw %}" />
        </div>
        <div class="tweet-text">{% raw %}{{tweet.text}}{% endraw %}</div>
        <div style="clear: both"></div>
      </div>
    </div>
  </body>
</html>

まぁ特にangular.js特有なの以外は無いので(ry

popup.js

var TwitterController = (function() {

  function TwitterController($scope, $http) {
    var bgPage = chrome.extension.getBackgroundPage();
    bgPage.authorize(function() {
      // APIエンドポイントにOAuthパラメーター関係を付与したURLを作る
      bgPage.signURL(
        "https://api.twitter.com/1.1/statuses/home_timeline.json",
        "GET",
        function(url) {
          $http.get(url).success(function(tweets) {
            $scope.tweets = tweets;
          });
        }
      );
    });
  }

  return TwitterController;
})();

angular.jsなコントローラーにchrome API依存するのもどうかとは思いますけど...

要は認証されていればTwitter APIからタイムラインを取得する。認証されてなければChromeExOAuth方式の認証フローに入るっていう感じ

background.js

var oauth = ChromeExOAuth.initBackgroundPage({
  consumer_key: CONSUMER_KEY,
  consumer_secret: CONSUMER_SECRET,
  request_url: "https://api.twitter.com/oauth/request_token",
  authorize_url: "https://api.twitter.com/oauth/authorize",
  access_url: "https://api.twitter.com/oauth/access_token",
  scope: "_chrome_extension_angularjs_twitter_oauth_demo"
});
oauth.callback_page = "callback.html";

window.authorize = function(callback) {
  oauth.authorize(callback);
};

window.signURL = function(url, method, callback) {
  var url = oauth.signURL(url, method);
  callback(url);
};

chrome.runtime.onMessage.addListener(function(req, sender, res) {
  oauth.getAccessToken(oauth.getToken(), req.verifier, function() {
    res(true);
  });

  return true;
});

コンテントスクリプトから来たauthorizeをverifyするPINコードを受け取ったらそれを利用してアクセストークンを取得する

callback.html

<html>
  <head>
    <script src="js/chrome_ex_oauth.js"></script>
    <script src="js/chrome_ex_oauthsimple.js"></script>
  </head>
  <body>
    <script src="callback.js"></script>
  </body>
</html>

callback.js

ChromeExOAuth.initCallbackPage();

ただのChromeExOAuthによる認証フロー開始するだけなので(ry

content_script.js

var pin = prompt("Enter the PIN displayed by Twitter");
chrome.runtime.sendMessage({ "verifier": pin }, function(isSuccess) {
  if (isSuccess === true) {
    alert("Authorized, woot!");
  }
});

とここまでがChrome拡張になる訳。あとはビルド(crx生成)するなりで使う訳ですが、そのままでは動かない。chrome_ex_oauth.jsに所々問題があって動かない箇所があるので修正する必要がある

chrome_ex_oauth.jsの修正

487行目辺り

parameters: {
  "xoauth_displayname" : this.app_name,
  "scope" : this.oauth_scope,
  // "oauth_callback" : url_callback
}

oauth_callbackが入ってるとリクエストトークンを取得する時点でエラー起こすのでoauth_callbackをパラメーターとして入れないように

514行目辺り

var token = params["oauth_token"];
this.setToken(token);

コンテントスクリプトからバックグランドページにsendMessageを行なってアクセストークンを取得するフローにおいてトークンが必要となるがトークン鍵はlocalStorageに保管されているが、トークン自体は保管されていないのでフロー上でundefinedになってしまう。なのでトークンもlocalStorageに保管しておく

但し、認証状態がtrueになるのはlocalStorageにトークンとトークン鍵があるかがチェックされるので、認証フローが途中で失敗した場合等にはChromeExOAuth#clearTokensを利用して取得しているリクエストトークンを保管しておかない等の工夫が若干必要になる

あくまでこの修正が必要なのはchrome_ex_oauthでTwitter APIを使う場合にのみ限られるのでは無いかと(もしかしたらDropboxも)

以上を行なって拡張機能をインストールして認証すると

っていうようにchrome_ex_oauthでOAuthを利用したREST API等をChrome Extensionでangular.jsを使いつつ出来るっていう感じで

slf4j+fluentd