POEed OpenID consumer Server
Web API で遊び倒す
などという何だかよくわからない演目を某Conference用に出してしまった私.いつものように,妙な演目を考えた自分を呪いながら四苦八苦しております.web apiと言っても,もしかしたらConference会場ではINTERNETに抜けられない状況があるかもしれないし,はたまたMashup Awardに出したら審査員が一瞥の下にゴミ箱に突っ込んでしまうようなwebアプリを作るってのもしゃくに触ります.そんなこんなで悩んだあげく,OpenIDを使って何か作ってみようと思い立ちました.
Net::OpenID::Consumer
とりあえず,と思ってCPANを漁って見ると,あるじゃあありませんか.その名も"Net::OpenID::Consumer".っても,その他に"Net::OpenID::JanRain::Consumer"とか"Net::OpenID::Consumer::Yadis"なんてのも転がっています.でもまぁ,そこは勘で「これだ」と思ったモジュールを使ってみるのです.
間違ってたら,そんときに考えればいいんだし.
さてこの,"Net::OepnID::Consumer"作ったのはBrad Fitzpatrick
POE::Compoennt::Server::HTTP
POEでcgiみたいなものを動かして,あまつさえcookieのハンドリングなんてだれかやってるかしらん.と思った時にふと思い出したのがhttp://naoya.g.hatena.ne.jp/naoya/20061113/1163418064でした.HTTP::Request::AsCGIモジュールと組み合わせれば,CGIモジュールも使えるしcookieの取り扱いも大丈夫.こんなのちょちょいのちょいです.
・・・,うそです.茨の道でした.
え〜と,気を取り直して,これが作り上げたスクリプトです.
#!/usr/local/bin/perl use strict; use Net::OpenID::Consumer; use POE qw/Component::Server::HTTP/; use CGI qw(:standard); use CGI::Cookie; use Digest::SHA1 qw(sha1 sha1_hex); use HTTP::Request::AsCGI; use Smart::Comments; my $this_script = "http://bar.foo.co.jp:10080"; my $required_root = "http://bar.foo.co.jp:10080"; my $trust_root = "http://bar.foo.co.jp:10080"; my $aliases = POE::Component::Server::HTTP->new( Port => 10080, ContentHandler => { '/' => \&handler }, ); my %table; POE::Kernel->run; sub handler { my ( $req, $res ) = @_; my $c = HTTP::Request::AsCGI->new($req)->setup; my $q = CGI->new; my $con = Net::OpenID::Consumer->new( ua => LWP::UserAgent->new, cache => undef, args => $q, consumer_secret => \&mysecret, required_root => $required_root, ); if ( $q->cookie('openid') ) { ### processing cookie, openid... if ( $q->param('logout') ) { ### processing logout... $table{ $q->cookie('openid') } = undef; print $q->redirect( -url => $this_script, -cookie => new CGI::Cookie( -name => 'openid', -value => "", -expires => '-1d', ), ); $res->code(302); } else { ### processing default page... print $q->header; print $q->start_html( -title => "OpenID Test" ); print $q->h1("OpenID Verified"); print $q->start_form; print $q->submit('logout'); print $q->end_form; print $q->end_html; $res->code(200); } } elsif ( $q->param() ) { if ( $q->param('ret') ) { ### processing ret... if ( my $setup_url = $con->user_setup_url ) { ### RET: $setup_url print $q->redirect( -url => $setup_url ); $res->code(302); } elsif ( $con->user_cancel ) { ### processing cancel... print $q->redirect($this_script); $res->code(302); } elsif ( my $vident = $con->verified_identity ) { my $verified_url = $vident->url; ### VERIFIED: $verified_url $table{$verified_url} = ""; print $q->redirect( -url => $this_script, -cookie => new CGI::Cookie( -name => 'openid', -value => $verified_url ), ); $res->code(302); } } elsif ( $q->param('openid_url') ) { ### processing user input url... my $claimed = $con->claimed_identity( $q->param('openid_url') ); ### CLAIMED: $claimed my $check_url = $claimed->check_url( return_to => $this_script . "?ret=true", trust_root => $trust_root, ); ### CHECK: $check_url print $q->redirect( -url => $check_url ); $res->code(302); } } else { ### print login page... print $q->header; print $q->start_html( -title => "OpenID test" ); print $q->h1("OpenID TEST"); print $q->start_form( -name => "openid_url" ); print "enter OpenID: "; print $q->textfield( -name => 'openid_url', -size => 35, -style => 'background: url(http://stat.livejournal.com/img/openid-inputicon.gif) no-repeat; background-color: #fff; background-position: 0 50%; padding-left: 18px;' ); print $q->submit( -name => "verify" ); print $q->end_form; print $q->end_html; $res->code(200); } my $c_res = $c->restore->response; $res->content( $c_res->content ); $res->{_headers} = $c_res->{_headers}; $res->{_msg} = $c_res->{_msg}; $res->{_rc} = $c_res->{_rc}; return RC_OK; } sub mysecret { my $t = shift; $t = time() unless ($t); $t -= $t % 3600; return sha1_hex( " foo " . ( $t - $t % 3600 ) ); }
動き
- ユーザがアクセスすると,login画面を出します.そこにOpenID urlを書いてやります
- ユーザが指定したページをGetして,そこに書いてあるOepnID Server名を探します(claimed_identity)
- 戻り先は俺だ,と印をつけて(check_url)OpenID Serverにリダイレクトします
- OpenID Serverから,認証するんだったらここに飛べと行ってきたurl(user_setup_url)にリダイレクトします
- 認証結果がokかどうかをチェック(verified_identity)して自分自身にリダイレクトします
- これで終わり
ってな感じです.このスクリプトのテストにはvoxサービスのOpenID機能を使ってみました.POEの動作を追いかけるためにSmart::Commentモジュールも使っています.動きは,元のguestbookスクリプトをそっくりそのまま踏襲しています.もしかしたら,もっと小さく作れるかもしれないですが,それはまた後日.あ,POEの中でLWP::UserAgentモジュールなんか使いやがって!,ブロックしちゃうじゃないか,っていう意見が出てくるのは承知の上で作っています.まだNet::OpenID::Consumerの中を覗いてないのでそこまで追いきれていません.