tohokuaikiのチラシの裏

技術的ネタとか。

Chrome Extensionで、TwitterのOAuthを使って投稿したい時とか

色々とハマり。

TwitterJavaScriptによるOAuth

何気に今までPHPでやっててJavaScriptではやったことなかったかも。facebookJavaScriptばかりな気もするが。

ここが分かりやすかった。
OAuth 対応 Twitter クライアント作成メモ


実装は、Googleoauth.jsと、sha1.jsを使ってこんな感じ

var twitterApiKey = {
    "consumerKey"   : "xxxxxxxxxxxxxxxxx",
    "consumerSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "accessToken"   : "",
    "tokenSecret"   : ""
  };


var myTwitter = function(){
};

myTwitter.prototype.accessor = {
  consumerSecret: twitterApiKey.consumerSecret,
  tokenSecret:    twitterApiKey.tokenSecret,
};

myTwitter.prototype.oauthToken = {
    oauth_token: "",
    oauth_token_secret: "",
    oauth_callback_confirmed : ""
};
    
myTwitter.prototype.message = {
  method: "get", 
  action: "https://twitter.com/oauth/request_token", 
  parameters: { 
      oauth_signature_method : "HMAC-SHA1", 
      oauth_consumer_key     : twitterApiKey.consumerKey,
      oauth_callback         : chrome.extension.getURL("oauth_callback.html")
    }
};

myTwitter.prototype.get = function(){
    var self = this;
    
    OAuth.setTimestampAndNonce(this.message);
    OAuth.SignatureMethod.sign(this.message, this.accessor);
    var target = OAuth.addToURL(this.message.action, this.message.parameters);
    $.ajax({
      type: this.message.method,
      url: target
    }).success(function(data, status, xhr) {
        var _param = data.split('&');
        for (var i=0, j=_param.length; i<j; i++){
            var _token = _param[i].split("=");
            if (_token.length == 2){
                self.oauthToken[_token[0]] = _token[1];
            }
        }
        var authorize_url = "https://api.twitter.com/oauth/authorize";
        authorize_url += "?oauth_token=" + self.oauthToken.oauth_token;

     
    }).error(function(){
        console.log(arguments); 
    });
};

どこにcallbackされるねん??

困ったのがこれ。
Google Chrome 拡張機能でOAuthを利用する方法 - おし、プログラミングを見ると、manifest.jsonにbackend_pageを付ければいいのかな?と思いきや、このプロパティはmanifestのバージョン2ではなくなったみたい。

無くなったのだけど、oauth_callback.html をExtensionのルートに入れて、
chrome-extension://ppbaaebidfcpelnieckckdjpcoebenip/oauth_callback.html
とかやるとアクセスできる。

ということはこのページにcallbackさせればいいんだと、とりえあず、OAuth.addToURL()の第二引数がパラメータっぽいので「oauth_callback : chrome.extension.getURL("oauth_callback.html")」を追加。


だけど、Twitterの「アプリ使っていいですか?」からOKボタンを押しても戻ってこない。blankになる。。。。

あれ~と思ってデバッガ見るとなんかchrome-extension://invalid/に転送されてる・・・・。


エラーメッセージ調べてみると、manifest.json

    "web_accessible_resources": [
        "oauth_callback.html"
        ],

ってやってやらないと、Webからchrome-extension://に来た場合、ダメっぽい。付けたら無事当該のChromeExtensionページを開いた。

manifest.jsonのpermissions

クロスドメインなので、permissionsプロパティに

    "permissions": [
        "https://twitter.com/oauth/*",
        "https://api.twitter.com/1.1/*"
  ]

の2つを追加。

Access token, Access token secret を取得してローカルに保存

先ほどcallbackされたページには、URLのQueryStringに「oauth_verifier」が付いているので、それを取得。

これを使ってAccess token, Access token secretを取得する。今度は、
https://api.twitter.com/oauth/access_tokenに問い合わせる。

今後はこのAccessTokenを使っていくのでこれをローカルストレージに保存する。

account/verify_credentialsでややハマり

GETメソッドで使える現在のaccess tokenが有効であるかどうかをチェックするAPI

getDefaultMessage = function(){
    return {
      method: "get", 
      action: "",
      parameters: { 
          oauth_signature_method : "HMAC-SHA1", 
          oauth_consumer_key     : twitterApiKey.consumerKey,
          oauth_version          : "1.0"
        }
    };
};

var message = getDefaultMessage();
    
message.action = "https://api.twitter.com/1.1/account/verify_credentials.json";
message.parameters.oauth_token = oauth_token;

var accessor =   {
  consumerSecret: "コンシューマシークレットキー",
  tokenSecret:    "アクセストークンシークレット"
};

OAuth.setTimestampAndNonce(message); // ここでパラメータにnonceとtimestampを追加
OAuth.SignatureMethod.sign(message, accessor); // ここでシグネチャを追加
var target = OAuth.addToURL(message.action, message.parameters);
    
$.ajax({
    type: "get",
    url: target
});

ってやればいいんだけど、このときmessage.parametersに余計なQueryStringとなるものを入れてはいけない。getDefaultMessageメソッドで適当なものをどんどん追加してtwitterAPI側で取捨選択してくれると思ってたので、ハマった。

statuses/updateで大ハマり

投稿をするAPIstatuses/updateはPOSTメソッドなのである。

さて、認証に必要な7つのパラメータ
oauth_consumer_key
oauth_nonce
oauth_signature
oauth_signature_method
oauth_timestamp
oauth_token
oauth_version
はどうやって渡すのか?

POSTパラメータにしてもダメだった。Authorizationヘッダをoauth.jsでOAuth.getAuthorizationHeader("", message.parameters);として加えてもダメだった(これは本当は行けるのかもしれない。自分のやり方がダメだっただけで)。


正解はQueryStringにする。しかも、ツイート文章までQueryStringで渡す。信じられないがこれで行ける。せめてツイート文章はPOSTパラメータやろう・・・・と思ってたのが大ハマり。

なので、

    var message = {
      method: "POST", 
      action: "https://api.twitter.com/1.1/statuses/update.json",
      parameters: { 
        oauth_signature_method: "HMAC-SHA1", 
        oauth_consumer_key: "コンシューマーキー",
        oauth_token: "アクセストークン"
      }        
    };
    
    message.parameters.status = tweet;
    
    OAuth.setTimestampAndNonce(message);
    OAuth.SignatureMethod.sign(message, this.accessor);
    
    var target = OAuth.addToURL(message.action, message.parameters);

   
    $.ajax({
      type: message.method,
      url:  target
    })

こんな感じ。これでうまく行ってしまう。ウソだろー。って気がするが本当。

TwitterクライアントのOAuth対応(Javascript編) | tomatomax.netが非常に参考になりました。