mixiアプリとoauth
まずはここからOAuth.phpをダウンロードしてくる。
http://code.google.com/p/oauth/
http://oauth.googlecode.com/svn-history/r526/code/php/OAuth.php
http://developer.mixi.co.jp/appli/spec/pc/require_servers/
mixiの解説ページによるとリビジョン526を指定しているのでそれをダウンロード。
mixiが提供しているコードサンプル
require_once("OAuth.php"); class MixiSignatureMethod extends OAuthSignatureMethod_RSA_SHA1 { protected function fetch_public_cert(&$request) { return <<< EOD -----BEGIN CERTIFICATE----- MIIDfDCCAmSgAwIBAgIJAJU4Z27Mql6HMA0GCSqGSIb3DQEBBQUAMDIxCzAJBgNV BAYTAkpQMREwDwYDVQQKEwhtaXhpIEluYzEQMA4GA1UEAxMHbWl4aS5qcDAeFw0x MjAxMDYwMjEyMDNaFw0xNDAxMDUwMjEyMDNaMDIxCzAJBgNVBAYTAkpQMREwDwYD VQQKEwhtaXhpIEluYzEQMA4GA1UEAxMHbWl4aS5qcDCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAK7r10TPJ6ANhQ8lK7IvX7E8KlWf4hA7LfbSy4EYbQOr nTsNu52lnqPDnRrMhemWx1cSgOYSzMzW2b5OufSR8rGM/CVQBKmmKScsG2mG0KOX CdBJKBJv+vk9SV9zfPCLETiE9gqy86Tz9UxLQUKIN/Vwj2GpQ6wIaJQGxrUtkdo9 kRzVA+PrLz+1mdhcNnvu15IzKoqCYwYQDrT1XysvKt8GPkYPlVweBzawpGOBqrPi zmju7P8yUfqoaO9eJr4arV+dZVup4yGTQlmrHbe/em9+l4HEboD8kiqiUiGwO5Ap 3Ndtco4yfJDYXu4b6rVJAV+c8YZfyboJZqPIEzGTvoECAwEAAaOBlDCBkTAdBgNV HQ4EFgQUbwJb5soi8EcvovonJ/GS3Di8VngwYgYDVR0jBFswWYAUbwJb5soi8Ecv ovonJ/GS3Di8VnihNqQ0MDIxCzAJBgNVBAYTAkpQMREwDwYDVQQKEwhtaXhpIElu YzEQMA4GA1UEAxMHbWl4aS5qcIIJAJU4Z27Mql6HMAwGA1UdEwQFMAMBAf8wDQYJ KoZIhvcNAQEFBQADggEBAKmheYWIpvyoSz0eBkcmdeZMq+EgH2lrbdfIIkpZ86N9 TFk7whfSlpSsOPaTyJcjTyWOV7orMdsOHwDTdSeCFtIoatHbVsy/KNYQcO+zAI3f glJ7MjpmN94fv0fRluwzp9g3OL90cC6b4qbDogtttGg8d+jiZ0Y38lOg0EOjjVQp jocQn0etv9PxY9zzcqOcxJXr/S9GkQFyc4HqzPJwT+FNS44pf7NM5cG9Yr/IKeQh vhWPsX3bF9Br9o7nSrcTeJzm5u3JQ7Rt0qSX3RPW4XA6uqd4DjrBQqkzk3tc6S25 L1kBVeo29EXDETUnfGz6UuNcxuA6yfXl6l7ypSVpv90= -----END CERTIFICATE----- EOD; } } //Build a request object from the current request $request = OAuthRequest::from_request(null, null, array_merge($_GET, $_POST)); //Initialize the new signature method $signature_method = new MixiSignatureMethod(); //Check the request signature @$signature_valid = $signature_method->check_signature($request, null, null, $_GET["oauth_signature"]); //Build the output object $payload = array(); if ($signature_valid == true) { $payload["validated"] = "Success! The data was validated"; } else { $payload["validated"] = "This request was spoofed"; } //Add extra parameters to help debugging $payload["query"] = array_merge($_GET, $_POST); $payload["rawpost"] = file_get_contents("php://input"); //Return the response as JSON print(json_encode($payload));
オレが作った検証環境はこんな感じ
.
-- lib |
`-- OAuth.php |
-- rsa_priv.pem |
-- rsa_pub.pem |
検証用の秘密鍵と公開鍵のセットを作成した。
$ openssl genrsa -out rsa_priv.pem 1024 $ openssl rsa -in rsa_priv.pem -out rsa_pub.pem -outform PEM -pubout
webをドキュメントルートとしている。
test.phpの内容はこんな。
<?php ini_set('display_errors', 'On'); error_reporting(E_ALL); require_once("../lib/OAuth.php"); class MixiSignatureMethod extends OAuthSignatureMethod_RSA_SHA1 { protected function fetch_public_cert(&$request) { return file_get_contents('../rsa_pub.pem'); } protected function fetch_private_cert($request) { return file_get_contents('../rsa_priv.pem'); } } //Build a request object from the current request //$request = OAuthRequest::from_request(null, null, array_merge($_GET, $_POST)); $request = OAuthRequest::from_request('GET', 'http://mixi.jp/', array_merge($_GET, $_POST)); //Initialize the new signature method $signature_method = new MixiSignatureMethod(); //Build signature $signature = $signature_method->build_signature($request, null, null); //var_dump($signature); //Check signature $ok = $signature_method->check_signature($request, null, null, $signature); var_dump($ok);
これで何を検証したかというと
まず、mixiのサンプルコードは、
http://developer.mixi.co.jp/appli/spec/mob/validate-oauth-signature/
http://developer.mixi.co.jp/appli/spec/mob/2-legged-oauth/
ここで書かれているように、oauth_signatureという秘密鍵から
oauth仕様に則って生成した値を渡してくる。
では、そのoauth_signatureを自分で作ってみる。
OAuth.phpのどこでその生成処理をしているかというと、
RSA_SHA1を使うので、このクラスの中の、build_signatureメソッドで作られる。
上記のtest.phpでは、build_signatureメソッドで作られた
$signatureをcheck_signatureメソッドに渡して、
その戻り値がtrue or falseで、署名が一致されたかを確認できる。
class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {/*{{{*/ public function get_name() {/*{{{*/ return "RSA-SHA1"; }/*}}}*/ protected function fetch_public_cert(&$request) {/*{{{*/ // not implemented yet, ideas are: // (1) do a lookup in a table of trusted certs keyed off of consumer // (2) fetch via http using a url provided by the requester // (3) some sort of specific discovery code based on request // // either way should return a string representation of the certificate throw Exception("fetch_public_cert not implemented"); }/*}}}*/ protected function fetch_private_cert(&$request) {/*{{{*/ // not implemented yet, ideas are: // (1) do a lookup in a table of trusted certs keyed off of consumer // // either way should return a string representation of the certificate throw Exception("fetch_private_cert not implemented"); }/*}}}*/ public function build_signature(&$request, $consumer, $token) {/*{{{*/ $base_string = $request->get_signature_base_string(); // Fetch the private key cert based on the request $cert = $this->fetch_private_cert($request); //Pull the private key ID from the certificate $privatekeyid = openssl_get_privatekey($cert); //Check the computer signature against the one passed in the query $ok = openssl_sign($base_string, $signature, $privatekeyid); //Release the key resource openssl_free_key($privatekeyid); return base64_encode($signature); } /*}}}*/ public function check_signature(&$request, $consumer, $token, $signature) {/*{{{*/ $decoded_sig = base64_decode($signature); $base_string = $request->get_signature_base_string(); // Fetch the public key cert based on the request $cert = $this->fetch_public_cert($request); //Pull the public key ID from the certificate $publickeyid = openssl_get_publickey($cert); //Check the computer signature against the one passed in the query $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); //Release the key resource openssl_free_key($publickeyid); return $ok == 1; } /*}}}*/ }/*}}}*/
ブラウザからoauthに必要なパラメータを伴ったアクセスを試す。
mixiの解説ページにあるパラメータ例をそのまま渡してみる。
http://ryouichi31.xxxx.xxx/test.php?oauth_consumer_key=bc906fac81f581c3c96a&oauth_nonce=9dc8fbca0e51842e7449&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1254282755&oauth_version=1.0&opensocial_app_id=123&opensocial_owner_id=xxxxxxxx
これで、$signatureを表示すると、作成された署名の値を確認できる。
$okを表示すると、$signatureの値が公開鍵との一致を試した結果の
true or falseを確認できる。
mixiアプリから外部サーバへoauth認証を行う
上記の仕組みを理解したので、mixiアプリからoauth_signatureを伴ったリクエストを、
自分で構築したサーバのweb/oauth.phpに送信する。
oauth.phpは上記したmixiが提供してるphpスクリプトをそのまま記述しておく。
mixi_reqserver.xmlという名前で、mixiアプリとして登録。
mixi側のキャッシュ、ブラウザのキャッシュ双方を消去して、
ブラウザでこのmixiアプリの動作を確認。
<?xml version="1.0" encoding="UTF-8"?> <Module> <ModulePrefs title="community"> <Require feature="opensocial-0.8" /> </ModulePrefs> <Content type="html"><![CDATA[ <script type="text/javascript"> function init() { var url = "http://ryouichi31.xxxx.xxx/oauth.php"; var params = {}; params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED; gadgets.io.makeRequest(url, function(response) { // do something... console.log(response); }, params); } gadgets.util.registerOnLoadHandler(init); </script> ]]></Content> </Module>
上記のアプリは認証成功の場合、コンソールログのObjectの中に以下のようなデータが入ってる。
data: "{ "validated":"Success! The data was validated", "query":{ "opensocial_owner_id":"nkib85dcueuyx", "opensocial_viewer_id":"nkib85dcueuyx", "opensocial_app_id":"37068", "opensocial_app_url": "http:\/\/ryouichi31.xxxx.xxx\/sample\/mixi_reqserver.xml", "oauth_token":"", "oauth_consumer_key":"mixi.jp", "xoauth_signature_publickey":"sr_20120106", "oauth_signature_method":"RSA-SHA1", "oauth_nonce":"d101a7e3e2525e7a", "oauth_timestamp":"1333077504", "oauth_signature":"ItW5\/TnQglzTK9a4bC\/Izz3o9DDTI9oJoLp1vuNwNJJw07ZSZtUQ+K4gkY5UiOpCCCz+6uuu9FtrPIdRYs7YKiSPrRRP2sM9unRsxt6p\/tntjZ\/o+gv4+syJk1aeRy1o+Tj\/B0Wni+OZwuOnCHXC21sYSUhSiJjshDqafvhgWimHFttEmdNYuJuTFBwXILx2saRfbxnFRoUTzpEHIdv5A9m0uIswjOAGegLhroxiG8Qez\/QLqc02N4lZnmWx94wmr5NkFaw7jRs9CE82n5km\/Yb3cVQpHwez\/0zPEElq3uKbkNRikYk6VpD92Msct5hpCg8+jFEtR\/XnVmqUT5llXg==" }, "rawpost":"" }"