「iTunesで聴いてる曲をTwitterにポストする」にある、iTunesで聴いている曲をTwitterにポストするiTunes2Twitterというのがあるんですが、その後手直しをしてないっぽくてOAuthされてません。
そこで、javascriptで、OAuth対応しようと気軽に考えてたんですが、どうしてみクロスサイトスクリプトの問題やらなにやらで、JavaScriptだけでOAuth認証に対応させるのは断念しました。
Twitterに対するOAuth認証まわりの実装は、PHPでおかなうようにしてなんとかならんかなとゴニョゴニョいじってたら出来ました。なんというか、OAuth経由でのアクセスするProxyのようなものを作ったイメージになります。
ということで、iTunesとTwitterを連携するiTunes2TwitterのOAuth対応版を作ってみました。
とりあえず仕組みはいいので使ってみたい人はこちらへ。
PHPでのOAuth認証まわりは、PEAR の HTTP_OAuthを使う事にしました。そこら辺は、「PHPでTwitter APIのOAuthを使う方法まとめ」を参考にさせて頂きました。
今後の事も考えて、OAuth認証周りのコードは、foursqueareなどでも使い回しが効くようにベースクラスを作って、Twitterを利用する部分をサブクラスで作成する感じにしました。
基本的な流れとしては、なんかのリクエストを投げたときに、認証されてなかったらOAuth認証の手続きを踏んで、Twitterなどの認証画面を別ウィンドウで開いて、認証してもらったらウインドウをとじて再度リクエストを投げるという感じにしています。別ウインドウを開いたりとか、再度リクエストを投げる部分はPHP側でJavaScriptのコードを返してevalで実行してもらうようにしてます。
OAuth認証用のベースクラスは、こんな感じ。です。
<?php require_once 'HTTP/OAuth/Consumer.php'; /** * OAuth Proxyクラス * */ class OAuthProxy { /** * HTTP_OAuth_Consumerクラスのインスタンス */ public $consumer; /** * consumer key */ protected $consumer_key; /** * consumer secret */ protected $consumer_secret; /** * OAUTHのコールバックURL */ protected $callback_url; /** * requestトークン取得用URL */ protected $request_token_url; /** * access トークン取得用URL */ protected $access_token_url; /** * 認証用URL */ protected $oauth_url; /** * コンストラクタです。 */ function __construct($consumer_key, $consumer_secret, $callback_url = NULL) { // クラス変数の初期化 $this->consumer_key = $consumer_key; $this->consumer_secret = $consumer_secret; $this->callback_url = $callback_url; // Consumerクラスの生成 $this->consumer = new HTTP_OAuth_Consumer($this->consumer_key, $this->consumer_secret); $http_request = new HTTP_Request2(); $http_request->setConfig('ssl_verify_peer', false); $consumer_request = new HTTP_OAuth_Consumer_Request; $consumer_request->accept($http_request); $this->consumer->accept($consumer_request); } /** * API呼び出し用 */ public function requestMethod($action, $param, $callback) { } /** * 認証後のコールバックの処理をします。 */ public function receiveCallback($oauth_verifier, $callback) { try { // アクセストークンを取得する。 $verifier = $oauth_verifier; $this->consumer->setToken($_SESSION['request_token']); $this->consumer->setTokenSecret($_SESSION['request_token_secret']); $this->consumer->getAccessToken($this->access_token_url, $verifier); // セッションに格納しておく $_SESSION['access_token'] = $this->consumer->getToken(); $_SESSION['access_token_secret'] = $this->consumer->getTokenSecret(); } catch (Exception $e) { $this->error($e); } ?> <html> <head> <meta http-equiv="Content-Type" content="text/body; charset=UTF-8"> <title></title> <script type="text/javascript" src="http://labs.s-koichi.info/js/jquery.js" ></script> <script type="text/javascript"> $(function() { // 親ウインドウのfunction呼び出し。 <?php if(isset($_SESSION["pushback"])) { echo 'eval("window.opener.' . $_SESSION["pushback"] . '()");' . "\n"; unset($_SESSION["pushback"]); } ?> // 自分自身のウインドウを閉じる。 window.close(); }); </script> </head> <body> </body> </html> <?php } /** * トークンの取得をして、認証先のウインドウを表示します。 */ public function requestAuth($callback) { // リクエストトークンを取得する。 $this->consumer->getRequestToken($this->request_token_url, $this->callback_url); // セッションに格納する。 $_SESSION['request_token'] = $this->consumer->getToken(); $_SESSION['request_token_secret'] = $this->consumer->getTokenSecret(); // 認証用のURLを取得する $auth_url = $this->consumer->getAuthorizeUrl($this->oauth_url); // 認証用のウインドウをひらくJavascriptを返す $js = 'window.open("' . $auth_url . '", "oauth_child", "width=600 : height=400 : toolbar=no: status=yes: resizable=yes : location=no");'; header('Content-type: text/json; charset=utf-8'); $ret = array('js' => $js); $this->printResult(json_encode($ret), $callback); } /** * */ protected function error(Exception $e, $callback) { if($this->checkAuth($e)) { // 認証が通ってないと怒られた。 unset($_SESSION['access_token']); unset($_SESSION['access_token_secret']); $this->requestAuth(NULL); } else { $msg = $e->getMessage(); $ejs = array("js" => 'print "' . $msg . '";'); $this->printResult($ejs, $callback); } } /** * 認証が通ってるかどうかの確認用 * * @param e */ protected function checkAuth($e) { $msg = $e->getMessage(); return ($msg == "Could not authenticate with OAuth."); } /** * jsonp対応。callbackがある場合はjsonpで出力する。 * * @param json * @param callback */ protected function printResult($json, $callback) { if($callback != NULL) { print $callback . "(" . $json . ");"; } else { print $json; } } } ?>
Twitter対応用のサブクラス。今回は投稿とTLの取得のみ対応してます。
<?php require_once 'OAuthProxy.class.php'; require_once 'Services/Twitter.php'; /** * Twitter用のOAuth Proxy */ class TwitterOAuthProxy extends OAuthProxy { protected $request_token_url = 'https://twitter.com/oauth/request_token'; protected $access_token_url = 'https://twitter.com/oauth/access_token'; protected $oauth_url = 'https://twitter.com/oauth/authorize'; // Service_Twitterのインスタンス private $twitter; public function requestMethod($action, $param, $callback) { if($action == "update") { update($param->msg); } else { friends_timeline(); } } /** * TwitterにPOSTします。 */ public function update($msg) { try { // 初期処理 $this->setTwitter(); header('Content-type: text/json; charset=utf-8'); print json_encode($this->twitter->statuses->update($msg)); } catch (Services_Twitter_Exception $e) { error($e); } } /** * TLの取得をします。 */ public function friends_timeline() { try { // 初期処理 $this->setTwitter(); header('Content-type: text/json; charset=utf-8'); print json_encode($this->twitter->statuses->friends_timeline()); } catch (Services_Twitter_Exception $e) { $this->error($e); } } /** * OAuth認証まで行う。 */ private function setTwitter() { // インスタンス生成 $this->twitter = new Services_Twitter(); // OAuthのアクセストークンで、認証を行なう $oauth = new HTTP_OAuth_Consumer( $this->consumer_key, $this->consumer_secret , $_SESSION['access_token'], $_SESSION['access_token_secret']); $this->twitter->setOAuth($oauth); } } ?>
実際にjavascriptからアクセスするProxyのソースがこちら。Comsumer keyとComsumer secretは、取得済みのものを設定しておきます。
<?php require_once 'TwitterOAuthProxy.php'; $consumer_key = '取得した CONSUMER KEY'; $consumer_secret = '取得した CONSUMER SECRET'; session_start(); if(isset($_POST['pushback'])) { $_SESSION['pushback'] = $_POST['pushback']; } $oauthproxy = new TwitterOAuthProxy($consumer_key, $consumer_secret, 'http://labs.s-koichi.info/oauthproxy/itunes2twproxy.php'); if(isset($_GET['oauth_verifier'])) { // 認証後に、callback urlできたとき $oauthproxy->receiveCallback($_GET['oauth_verifier'], NULL); } else if(isset($_SESSION['access_token']) && isset($_SESSION['access_token_secret'])) { // access_tokenがもうあるとき。 $status = $_POST['status']; if($_POST['action'] == "update") { $oauthproxy->update($status); } else { $oauthproxy->friends_timeline(); } } else { // これから認証。 unset($_SESSION['access_token']); unset($_SESSION['access_token_secret']); $oauthproxy->requestAuth(NULL); } ?>
まぁ、エラー処理とかは、結構適当です。まだ、足りないところがあると思います。このProxyを使って、TLを取得サンプルを作ってみました。
<html> <head> <meta http-equiv="Content-Type" content="text/body; charset=UTF-8"> <title>OAuth sample</title> <script type="text/javascript" src="http://labs.s-koichi.info/js/jquery.js" ></script> <script type="text/javascript"> function post() { $.post("http://labs.s-koichi.info/oauthproxy/itunes2twproxy.php", {"pushback":"post", "action":"friend"}, function (data, status) { if(status == "success") { if(data.js == undefined) { $( data).each( function() { $("#result").append('<div>' + this.text + '</div>'); }); } else { eval(data.js); } } }, "json"); } </script> </head> <body> <button id="post" onClick="post()">post</button> <div id="result"></div> </body> </html>
動くサンプルは、こちらです。
そして、これを使ってiTunes2TwitterをOAuth対応したのがこちらです。
iTunesとActive Xを使ってアクセスしているので、IE専用です。iTunesで曲を流すと、曲名などが出ますが、何も表示されない場合は、設定を見なおしてみてください。>
「スクリプトを実行しても安全だとマークされていない Active Xコントロールの初期化とスクリプトの実行」が無効になっている場合は、有効にするか、ダイアログを表示するに変更して下さい。
動かしているときのスクリーンショットです。定期的にPOSTされますが、Twitterボタンを押せばすぐにPOST出来るのはオリジナルと同じ仕様です。 #listeningnowハッシュタグと、#nowplayingハッシュタグが付与されます。
foursquare API+Google Mapでマッシュアップ その1 « Koichi Labs Blog
[...] eのAPIを使っている部分は、phpでOAuth認証をさせています。先日のiTunes2Twitterで使ったクラスをfoursqueare用のサブクラスを作っています。スーパークラスは、先日のエントリーと同じです。 [...]
iTunes2Twitterを改良 | Koichi Labs Blog
[...] iTunesで聞いてる曲をTwitterにポストするiTunes2TwitterをOAuthしてみるエントリーを書いたんですが、基本的にオリジナルの動作を引き継いだものでした。 [...]