OpenpearでHTTP_OAuthProviderを公開してみた

公開した。
 
Openpearでパッケージを公開するとメールアドレスも公開されるという事をさっき知った。
もういいやと思って、文中のPear準拠のコメント部分で本名も晒した。
 
プログラムは去年書いたものをベースにしたんだけど、結構書き方も変わった。
なかなかいい感じの書き方が出来たんじゃないかと思ってる。
 
HTTP_OAuthProvider - Openpear
http://openpear.org/package/HTTP_OAuthProvider
 
/HTTP_OAuthProvider/trunk - リポジトリブラウザ - Openpear
http://openpear.org/repository/HTTP_OAuthProvider/trunk
 

機能

OAuthリクエストの認証を行うサービスプロバイダ側のライブラリ。
2Legged OAuthと3Legged OAuthの両方に対応。
対応しているシグネチャ方式は、HMAC-SHA1RSA-SHA1
トークンの保持に、Cache_Liteを使用。
トークン保持の方法は今後いくつか増やす予定。
 

インストール

Openpearのチャンネル追加。

[root@localhost ~]# pear channel-discover openpear.org
Adding Channel "openpear.org" succeeded
Discovery of channel "openpear.org" succeeded

 
HTTP_OAuthProviderのインストール。

[root@localhost ~]# pear install openpear/HTTP_OAuthProvider
downloading HTTP_OAuthProvider-1.0.0.tgz ...
Starting to download HTTP_OAuthProvider-1.0.0.tgz (6,719 bytes)
.....done: 6,719 bytes
install ok: channel://openpear.org/HTTP_OAuthProvider-1.0.0

 

サンプル

現時点でここに書いてあるものと同じだけど…。
 
/HTTP_OAuthProvider/trunk/example - リポジトリブラウザ - Openpear
http://openpear.org/repository/HTTP_OAuthProvider/trunk/example
 

2Legged OAuth認証を行う
<?php
require_once('HTTP/OAuthProvider.php');
require_once('config.php');

$o = new HTTP_OAuthProvider();
try {
	$o->setFetchConsumerHandler('fetchConsumer');
	$o->authenticate();
	echo "Auth OK!!!\n";
	printf("consumer: %s\n", $o->getConsumer()->getKey());

} catch(Exception $e) {
	header(sprintf('HTTP/1.0 %d', $e->getCode()));
	echo $e->getMessage();
}

 

リクエストークンを発行する
<?php
require_once('HTTP/OAuthProvider.php');
require_once('config.php');

$o = new HTTP_OAuthProvider();
try {
	$o->setFetchConsumerHandler('fetchConsumer');
	echo $o->issueRequestToken();

} catch(Exception $e) {
	header(sprintf('HTTP/1.0 %d', $e->getCode()));
	echo $e->getMessage();
}

 

ユーザの認可を保存する(フォームも表示)
<?php
require_once('HTTP/OAuthProvider.php');
require_once('config.php');

$content = "<b>this is provider's page !!</b><br />\n";
$o = new HTTP_OAuthProvider();
try {
	$o->setFetchConsumerHandler('fetchConsumer');

	// check request token
	if (!$o->existsRequestToken()) {
		throw new Exception('not found request token', 200);
	}

	// show callback url
	if (isset($_REQUEST['authorize_confirm'])) {
		if ($_REQUEST['authorize_confirm']) {
			$callback = $o->authorizeToken($user_id, true);
			$content .= "you choose agree<br />\n";
		} else {
			$callback = $o->authorizeToken($user_id, false);
			$content .= "you choose disagree<br />\n";
		}
		$content .= "return to consumer's page<br />\n";
		$content .= sprintf('<a href="%s">%s</a>', $callback, $callback)."<br />\n";

	// show form
	} else {
		$content .= sprintf("hello. user id %s !<br />\n", $user_id);
		$content .= "do you authorize the consumer?<br />\n";
		$content .= sprintf("consumer is %s<br />\n", $o->getConsumer()->getKey());

		// agree form
		$content .= '<form action="?" method="post">'."\n";
		$content .= sprintf('<input type="hidden" name="oauth_token" value="%s" />', $_REQUEST['oauth_token'])."\n";
		$content .= '<input type="hidden" name="authorize_confirm" value="1" />'."\n";
		$content .= sprintf('<input type="submit" value="agree">')."\n";
		$content .= "</form>\n";

		// disagree form
		$content .= '<form action="?" method="post">'."\n";
		$content .= sprintf('<input type="hidden" name="oauth_token" value="%s" />', $_REQUEST['oauth_token'])."\n";
		$content .= '<input type="hidden" name="authorize_confirm" value="0" />'."\n";
		$content .= sprintf('<input type="submit" value="disagree">')."\n";
		$content .= "</form>\n";
	}

} catch(Exception $e) {
	header(sprintf('HTTP/1.0 %d', $e->getCode()));
	$content .= $e->getMessage();
}

?>

<html>
<head>
<title>provider's page</title>
</head>
<body>
<?php echo $content; ?>
</body>
<html>

 

認可されたリクエストークンをアクセストークンに交換する
<?php
require_once('HTTP/OAuthProvider.php');
require_once('config.php');

$o = new HTTP_OAuthProvider();
try {
	$o->setFetchConsumerHandler('fetchConsumer');
	echo $o->exchangeAccessToken();

} catch(Exception $e) {
	header(sprintf('HTTP/1.0 %d', $e->getCode()));
	echo $e->getMessage();
}

 

保護されたリソースへのアクセスの認証を行う
<?php
require_once('HTTP/OAuthProvider.php');
require_once('config.php');

$o = new HTTP_OAuthProvider();
try {
	$o->setFetchConsumerHandler('fetchConsumer');
	$o->authenticate3L();
	echo "this is provider's protected resource !!\n";
	printf("consumer: %s\n", $o->getConsumer()->getKey());
	printf("user_id: %s\n", $o->getStore()->getUserID());

} catch(Exception $e) {
	header(sprintf('HTTP/1.0 %d', $e->getCode()));
	echo $e->getMessage();
}

 

設定ファイル

コンシューマもユーザも固定。
コンシューマは1人だけいる想定。
ユーザはID12345がログイン済みという想定。

<?php

// consumer

function fetchConsumer($consumer_key)
{
	$row = array(
		'id' => 1,
		'key' => 'testconsumer',
		'secret' => 'testpass',
		'publickey' => null
	);
	if ($consumer_key==$row['key']) {
		$consumer = new HTTP_OAuthProvider_Consumer($row);
		return $consumer;
	}
}


// user

$user_id = 12345;

 

テスト用コンシューマ
<?php
// require HTTP_OAuth
require_once('HTTP/OAuth/Consumer.php');

/* 認証情報を格納するためにセッションを開始 */
session_start();

/* Consumer key */
$consumer_key = 'testconsumer';
/* Consumer Secret */
$consumer_secret = 'testpass';

/* プロバイダからの Callback url */
$callback_url = sprintf('http://%s%s', $_SERVER['HTTP_HOST'], $_SERVER['SCRIPT_NAME']);

$provider_base = 'http://example.com/example/';
$request_url = $provider_base.'3legged_request.php';
$authorize_url = $provider_base.'3legged_authorize.php';
$access_url = $provider_base.'3legged_access.php';
$resource_url = $provider_base.'3legged_resource.php';


/* Set up placeholder */
$content = '';

/* セッションのクリア */
if (@$_REQUEST['test'] === 'clear') {
    session_destroy();
    session_start();
}

try {

    // -- init HTTP_OAuth_Consumer
    $oauth = new HTTP_OAuth_Consumer($consumer_key, $consumer_secret);
    // ssl通信を可能に
    $http_request = new HTTP_Request2();
    $http_request->setConfig('ssl_verify_peer', false);
    $consumer_request = new HTTP_OAuth_Consumer_Request;
    $consumer_request->accept($http_request);
    $oauth->accept($consumer_request);

    if (!empty($_REQUEST['oauth_token']) && $_SESSION['oauth_state'] === 'start') {
        // -- プロバイダから認証後に戻ってきた場合 (callback処理)
        $_SESSION['oauth_state'] = 'returned';

        if (empty($_SESSION['oauth_access_token']) || empty($_SESSION['oauth_access_token_secret'])) {
            // -- access_tokenが未取得の場合
            /* request tokenをセット */
            $oauth->setToken($_SESSION['oauth_request_token']);
            $oauth->setTokenSecret($_SESSION['oauth_request_token_secret']);

            /* プロバイダから戻ってきた oauth_verifierをセット */
            $oauth_verifier = $_REQUEST['oauth_verifier'];

            /* Access token をリクエスト */
            $oauth->getAccessToken($access_url, $oauth_verifier);

            /* Acces tokenを保存 (実際のアプリケーションではこれをDB等に保存しておきます。) */
            $_SESSION['oauth_access_token'] = $oauth->getToken();
            $_SESSION['oauth_access_token_secret'] = $oauth->getTokenSecret();
        }

    }

    if (!empty($_SESSION['oauth_access_token']) && !empty($_SESSION['oauth_access_token_secret'])) {
        // -- 認証済みの場合

        /* access_tokenをセット */
        $oauth->setToken($_SESSION['oauth_access_token']);
        $oauth->setTokenSecret($_SESSION['oauth_access_token_secret']);

        /* ユーザ情報を取得するリクエストを発行. */
        $result = $oauth->sendRequest($resource_url, array(), 'GET');

        /* データを取得 */
        $content = $result->getBody();

    } else {

        // -- 初回呼び出し時
        /* プロバイダからrequest_tokenの取得 */
        $oauth->getRequestToken($request_url, $callback_url);

        /* tokenをセッションに保存 */
        $_SESSION['oauth_request_token'] = $oauth->getToken();
        $_SESSION['oauth_request_token_secret'] = $oauth->getTokenSecret();
        /* ステータスをstartにセット */
        $_SESSION['oauth_state'] = "start";

        /* authorization URL を取得 */
        $request_link = $oauth->getAuthorizeURL($authorize_url);

        /* authorization URLのリンクを作成 */
        $content = 'Click on the link to go to provider to authorize your account.';
        $content .= '<br /><a href="'.$request_link.'">'.$request_link.'</a>';

    }

} catch (Exception $e) {
    $content = $e->getMessage();
}
?>

<html>
<head>
<title>OAuth in PHP</title>
</head>
<body>
<h2>Welcome to a OAuth PHP example.</h2>
<p><a href='<?php echo $_SERVER['PHP_SELF']; ?>?test=clear'>clear sessions</a></p>

<p><pre><?php print_r($content); ?><pre></p>

</body>
</html>

 
3Legged OAuthのコンシューマ側のサンプルは、ここから拝借した。
 
HTTP_OAuthを利用してTwitterOAuth認証を行う @ php-tips
http://php-tips.com/php/2009/11/http_oauth%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6twitter%E3%81%AEoauth%E8%AA%8D%E8%A8%BC%E3%82%92%E8%A1%8C%E3%81%86
 

OAuthの仕様がよく解らない人用URL

OAuthプロトコルの中身をざっくり解説してみるよ - ゆろよろ日記
http://d.hatena.ne.jp/yuroyoro/20100506/1273137673
 
PHPを読んだことある人のためのOAuthのSignature解説 - r-weblife
http://d.hatena.ne.jp/ritou/20090912/1252776563
 
OpenSocialのサービスプロバイダっぽいのを作ってみる 5.OAuth Core 1.0a(3legged OAuth)の仕様 - eth0jpの日記
http://d.hatena.ne.jp/eth0jp/20090826/1251228344
 
OpenSocialのサービスプロバイダっぽいのを作ってみる 6.OAuth Core 1.0a(3legged OAuth)を実装してみた - eth0jpの日記
http://d.hatena.ne.jp/eth0jp/20090829/1251497285
 
「OAuth Core 1.0 Revision A」日本語訳をつくってみた - tzmtkのブログ
http://d.hatena.ne.jp/tzmtk/20090723/p1
 
OAuth Core 1.0a
http://oauth.net/core/1.0a/
 

TODO

ちゃんとPear準拠の書き方をして、本家Pearにパッケージ追加の申請出してみようかなぁ。
ただ、英語がめんどくさい。
あと、1行の長さに対する制限が厳しすぎると思う。