APIの認証ってどうやってるんだろーって思っているときに、Google Dev Quizで2-legged OAuthの問題があったので、やりながら理解する事にした。perlで書いてみました。
OAuthのcpanモジュールを使った場合
この場合、簡単にできます。OAuth::Lite::Consumerにconsumer_keyとconsumer_secretとrealmを渡して作成してやった後、method, url, paramsを指定してrequestするだけです。timestamp値やnonceなどはすべて自動で付けて、署名を作ってくれています。
use strict; use warnings; use OAuth::Lite; use OAuth::Lite::Consumer; my $ua = LWP::UserAgent->new; my $request_url = 'http://gdd-2010-quiz-japan.appspot.com/oauth/hogehogehoge'; my $consumer = OAuth::Lite::Consumer->new( consumer_key => 'hogehoge', consumer_secret => 'piyopiyo', realm => 'devquiz', ); my $res = $consumer->request( method => 'POST', url => $request_url, params => {hello => 'world'}, ); use YAML; warn YAML::Dump($res);
OAuthモジュールを使わない場合
さすがに上のコードでは、ラップされすぎていて理解できなかったので、OAuthモジュールを使わずになんとか自分でやってみました。適当にいじっていったので、むちゃくちゃ汚いコードになっていますが、下のようになりました。
use strict; use warnings; use String::Random; use DateTime; use URI::Escape; use Digest::HMAC_SHA1 qw(hmac_sha1); use MIME::Base64; use LWP::UserAgent; my $realm = 'devquiz'; # realm my $param = { hello => 'world' }; # 送りたいparameter my $url = 'http://gdd-2010-quiz-japan.appspot.com/oauth/hogehogehoge'; # 送りたいurl my $oauth_consumer_secret = 'piyopiyp'; # 秘密鍵 my $oauth_param = { oauth_consumer_key => 'hogehoge', oauth_nonce => String::Random->new->randregex('\w{20}'), oauth_timestamp => DateTime->now->epoch, oauth_version => '1.0', oauth_signature_method => 'HMAC-SHA1' }; # Authorizationヘッダの作成 my $header = join(', ', map { $_ . '=' . '"' . $oauth_param->{$_} . '"'; } keys %$oauth_param); $header = qq{OAuth realm="${realm}", } . $header; # signatureの為のベース文字列を作成 my $signature_string = "POST&" . uri_escape($url); my $signature_param = { %$oauth_param, %$param, }; my $parameter_string .= join('&', map { $_ . '=' . $signature_param->{$_}; } sort keys %$signature_param); $signature_string .= '&' . uri_escape($parameter_string); # signatureの作成 my $hmac_sha1_key = $oauth_consumer_secret . '&'; $signature_string = hmac_sha1($signature_string, $hmac_sha1_key); $signature_string = encode_base64($signature_string); chomp $signature_string; # なぜか改行コードが追加されてる...orz # headerへ代入 $header .= ', oauth_signature="' . $signature_string . '"'; # 送信 my $ua = LWP::UserAgent->new; my $res = $ua->post($url, $param, 'Authorization' => $header); use YAML; warn YAML::Dump($res);
2-legged OAuthの手順
モジュールを使わずにやってみたら、手順が理解できたので、まとめておきます。
1.様々なパラメータから署名を作成する
まず以下の三つの文字列を&で繋いだベース文字列を作成する。
- リクエストメソッド名(GET, POSTなど)
- リクエストURLをuri_escapeしたもの(GETの場合パラメータは除く)
- consumer_secretとrealm以外のOAuthパラメータと送信したいパラメータを、キー名=値という形にして&でつなげ、uri_escapeしたもの。ただし、キー名でソートした順番になっていなければならない。
- 今回だったら次のようになります。「hello%3Dworld%26oauth_consumer_key%3Dhogehoge%26oauth_nonce%3DRJQX3sK5nIodUcRS426k%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1282447259%26oauth_version%3D1.0」
次にconsumer_secretとtokenを&で繋いだものをキーとして、hmac_sha1でダイジェスト値を作ります。今回だったらtokenは空なので「piyopiyo&」のような文字列をキーとします。
2.Authorizationヘッダを作成する
oauthに必要な情報を「OAuth キー名="値", キー名="値", ...」というように繋げた文字列を作ります。今回であれば必要な情報は下の通りです。
- oauth_consumer_key
- oauth_nonce
- oauth_timestamp
- oauth_version
- oauth_signature_method
- realm
- oauth_signature
ヘッダとしては以下のようになりました。
OAuth realm="devquiz", oauth_timestamp="1282447809", oauth_nonce="nVj2Up8xkA2fJkyGLE4X", oauth_consumer_key="hogehoge", oauth_version="1.0", oauth_signature_method="HMAC-SHA1", oauth_signature="lKWlM2ZnyCAmCDOSIMZGSyNB4Nc="