http://d.hatena.ne.jp/end0tknr/20131215/1387105372
先日のエントリの続き、Net::OpenID::Consumerに加え、Net::OpenID::Serverを使って、OpenIDのOP(OpenID Provider , server)とRP(Relying Party , client )を連携。
Net::OpenID::Server - search.cpan.org
Net::OpenID::Consumer - search.cpan.org
参考url
たけまる / OpenID v1.1 の IdP と Consumer を Catalyst で動かした
さかなかな: OpenID at perl Net::OpenID::Consumer & Net::OpenID::Server
OpenIDのxrds文書の正しい書き方 | のぶろぐ
http://blog.yappo.jp/yappo/archives/000563.html
Final: OpenID Authentication 2.0 - 最終版
サイト構成( apache on coliux port:8081 )
今回はcolinux上のapache(port:8081)でRP, OPのそれぞれを動かします。
ディレクトリ構成
opid/ opid.pl server.xrds opid_c/ openid_client.pl opid_c/ .htaccess openid_server.pl
httpd.conf抜粋
AddType application/xrds+xml .xrds Alias /server.xrds /home/endo/dev/opid/server.xrds #ex. OpenIDの例=http://colinux.a4.jp:8081/opid/end0tknr Alias /opid /home/endo/dev/opid/opid.pl <Directory "/home/endo/dev/opid"> AllowOverride All <Files "*.pl"> Options ExecCGI AddHandler cgi-script .pl </Files> </Directory> #LoadModule headers_module modules/mod_headers.so #<Location /> # Header add X-XRDS-Location http://colinux.a4.jp:8081/server.xrds #</Location> Alias /opid_c /home/endo/dev/opid_c <Directory "/home/endo/dev/opid_c"> AllowOverride All <Files "*.pl"> Options ExecCGI AddHandler cgi-script .pl </Files> </Directory> Alias /opid_s /home/endo/dev/opid_s <Directory "/home/endo/dev/opid_s"> AllowOverride All <Files "*.pl"> Options ExecCGI AddHandler cgi-script .pl </Files> </Directory>
ディレクトリ opid_c (OpenID Client)
openid_client.pl
http://d.hatena.ne.jp/end0tknr/20131215/1387105372
OpenIDのclient側は、以前のエントリに記載している通リなので、詳細は記載しません
#!/usr/local/bin/perl use strict; use CGI; use Net::OpenID::Consumer; #use LWPx::ParanoidAgent; use LWP::UserAgent; use Data::Dumper; main(); sub main { my $query = CGI->new; $query->charset('utf-8'); # User-AgentにLWPx::ParanoidAgent を使用する理由は次のurlが分かりやすい. # 私の場合、colinux環境でこのOpenID Relying Party を試していますが # colinux環境はDNSに登録されていない為か LWPx::ParanoidAgent を使用すると # errorになるので、LWP::UserAgent を使用しています # http://www.atmarkit.co.jp/ait/articles/0711/20/news128.html my $ua = LWP::UserAgent->new; # my $ua = LWPx::ParanoidAgent->new(); my $csr = Net::OpenID::Consumer->new(ua => $ua, args => $query, consumer_secret =>sub{$_[0]}, #TODO? #srcを読むと、debug optionがあるみたい # debug => 1 ); my $claimed_url = $query->param('openid-url'); #### step2 userが入力したOpenID(url)を identity provider に認証依頼 if ($claimed_url) { request_to_identity_provider($query,$csr,$claimed_url); return; } if ($query->param('verify')) { #### step? http://www.atmarkit.co.jp/ait/articles/0709/21/news142_3.html if ($csr->user_cancel) { show_login_form($query, 'your login is cancelled.'); return; } #### step3 次のurlに記載されている通り。 # checkid_immediateモードでopenid.user_setup_urlがIdPから返される場合 # 再びUser-Agentをその指定URLにリダイレクトさせなければならないらしい # http://www.atmarkit.co.jp/ait/articles/0709/21/news142_3.html # http://d.hatena.ne.jp/clouder/20081226/p1 if (my $setup_url = $csr->user_setup_url) { print $query->redirect(-uri => $setup_url); return; } my $id_tmp = $csr->verified_identity; #### step4 IDENTITY PROVIDERによる認証完了 if (my $identity = $csr->verified_identity) { # my $user = +{ map { $_ => scalar $identity->$_ } # qw( url display rss atom foaf declared_rss # declared_atom declared_foaf foafmaker ) }; print $query->header,"\n"; print "<html><head></head><body>\n"; print "<h1>OpenID logged in !!</h1>"; print "<pre>VERIFIED_IDENTITY(OpenID, url)\n", $identity->url , "</pre>"; print "<pre>", Dumper($identity) , "</pre>"; print "</body></html>"; return; } show_login_form($query); return; } #### step1 userがlogin formでopen_id(url)を入力 show_login_form($query); } sub request_to_identity_provider { my ($query,$csr,$claimed_url) = @_; my $identity = $csr->claimed_identity($claimed_url); unless($identity){ show_login_form($query, 'wrong identity'. Dumper($csr)); return; } my $check_url = $identity->check_url(return_to=> URI->new($query->url.'?verify=1')->as_string, trust_root => $query->url); print $query->redirect(-uri => $check_url); return; } sub show_login_form { my ($query, $message) = @_; print $query->header, <<PAGE; <html> <head></head> <body> <form action='openid_client.pl' method='POST'> INPUT your OpenID (url) <input name='openid-url' maxlength='100' style="width:500px;" value="http://colinux.a4.jp:8081/opid/end0tknr" /> <input type='submit' value='login by OpenID' /><br/><br/> ex.) https://me.yahoo.co.jp/a/XXXXXXXXXXXXX<br> ex.) http://colinux.a4.jp:8081/opid/end0tknr<br> http://help.yahoo.co.jp/help/jp/edit/openid <pre style="margin:5px;padding:5px;border:1px solid #000"> $message</pre> </form> </body></html> PAGE } 1; __END__
ディレクトリ opid
opid.pl
今回は、OpenID(identity url)を http://colinux.a4.jp:8081/opid/end0tknr としていますが、先程の openid_client.pl は、まず、このOpenIDをhttp getし、httpヘッダにあるxrdsファイルの場所を特定し、OpenIDの認証手続きへ進みます。
#!/usr/local/bin/perl use strict; use CGI; use Net::OpenID::Server; use Data::Dumper; main(); sub main { my $cgi = CGI->new; print $cgi->header("-X-XRDS-Location"=> "http://colinux.a4.jp:8081/server.xrds"); }
私の場合、CGI.pmのheader()でX-XRDS-Locationを追加していますが、apacheのhttpd.confに次のように記載することで、同様にX-XRDS-Locationを追加することもできます。
LoadModule headers_module modules/mod_headers.so <Location /> Header add X-XRDS-Location http://colinux.a4.jp:8081/server.xrds </Location>
※mod_headers.so がない場合、apacheへmoduleを追加インストールして下さい
server.xrds
xrdsファイルには、OpenID Providerの認証仕様?や、実際に認証手続きを行うend pointの場所(今回の場合、openid_server.pl)を記載します。
<?xml version="1.0" encoding="UTF-8"?> <xrds:XRDS xmlns:xrds="xri://$xrds" xmlns:openid="http://openid.net/xmlns/1.0" xmlns="xri://$xrd*($v*2.0)"> <XRD> <Service priority="0"> <Type>http://specs.openid.net/auth/2.0/signon</Type> <Type>http://specs.openid.net/extensions/pape/1.0</Type> <Type>http://openid.net/srv/ax/1.0</Type> <Type>http://specs.openid.net/extensions/ui/1.0/mode/popup</Type> <Type>http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf</Type> <URI>http://colinux.a4.jp:8081/opid_s/openid_server.pl</URI> </Service> </XRD> </xrds:XRDS>
※私の場合、yahooのxrdsファイルを参考にさせて頂きました。
https://open.login.yahooapis.jp/openid20/user_profile/xrds
ディレクトリ opid_s (OpenID Server)
.htaccess
opid_sでは環境変数:REMOTE_USERによりユーザIDを取得する為、basic認証を利用します。
また、OpenIDの認証手続きでは、openid_client.pl と openid_server.pl でのサーバ間直接連携も必要ですので、openid_client.pl ( 192.168.137.5 ) からのaccessは認証不要としています。
AuthType Basic AuthName "Restricted Files" AuthUserFile /home/endo/dev/htpasswd Require valid-user Satisfy any order deny,allow allow from 192.168.137.5 allow from 127.0.0.1 deny from all
openid_server.pl
#!/usr/local/bin/perl use strict; use CGI; use Net::OpenID::Server; use Data::Dumper; my $cgi = CGI->new; my $nos = Net::OpenID::Server->new( args => $cgi, get_user => \&get_user, get_identity => \&get_identity, is_identity => \&is_identity, is_trusted => \&is_trusted, server_secret => 'some_random_sequence_put_your_own', setup_url => "http://colinux.a4.jp:8081/opid_s/openid_server.pl", endpoint_url => "http://colinux.a4.jp:8081/opid_s/openid_server.pl", ); my ($type, $data) = $nos->handle_page; if ($type eq "redirect") { #この部分は、ユーザ(ブラウザ)から呼ばれます my $trust = $cgi->param('openid.realm') || $cgi->param('openid.trust_root'); print $cgi->header, $cgi->start_html(sprintf 'OpenID TEST'), sprintf(q|<h1>do you should login to <a href="">%s</a>?</h1>|, $trust, $trust), qq|<a href="$data">login</a>|, $cgi->end_html; } elsif ($type eq "setup") { # my %setup_opts = %$data; # ... show them setup page(s), with options from setup_map # it's then your job to redirect them at the end to "return_to" # (or whatever you've named it in setup_map) } else { #この部分は、openid_client.plから直接呼ばれます print $cgi->header($type); print $data; print STDERR "$data\n" } sub get_user { return $ENV{REMOTE_USER}; } sub get_identity { my($user, $identity) = @_; return "http://colinux.a4.jp:8081/opid/$user"; } sub is_identity { my($user, $identity) = @_; my $ret = $user && $user eq (split '/', $identity)[-1]; return $ret; } sub is_trusted { my($user, $trust_root, $is_identity) = @_; return $is_identity; }