遥かへのスピードランナー

シリコンバレーでAndroidアプリの開発してます。コンピュータービジョン・3D・アルゴリズム界隈にもたまに出現します。

mixi OpenIDのコミュニティ認証をバックグラウンドで行うプログラムを組んでみた

mixiOpenIDはブラウザのリダイレクトで認証を行うので、1画面で1回の認証しかできないと思われがちですが、これを回避するプログラムを組んでみました。

mixi openIDでは、「この外部サイトの場合常に同意する」を選択していれば、ブラウザ操作なしに認証後URLまで自動で遷移されるので、複数のiframeの中でopenIDの認証を行うことによって、複数のコミュニティ認証、あるいは複数のmixiユーザー認証をバックグラウンドで行うことが可能です。(ただし、認証はリダイレクトだけでなく、途中でmeta refreshが入るのでXMLHttpRequestや、scriptのsrcの中で認証することなどはできない。iframeが限度じゃないかとおもってます。)

以下それをテストするためのスクリプトを書いてみました。

デモURL

こちらです⇒http://poly.s49.xrea.com/dev.ohirune.net/mixi/mixi_auth.cgi
mixi OpenID利用同意画面が表示されたら、「この外部サイトの場合常に同意する」を選択してください。(常に同意したくない場合は試せませんw)
以下のmixiコミュニティに登録している場合、それぞれに対応したyoutube動画が表示されます。

IE6.0とFirefox2.0、Firefox3.0で動作確認しています。

前提条件

サーバー側に以下のPerlモジュールをインストールしておく必要があります。

スクリプト(グローバル変数つかいまくりで結構汚いです。。)

mixi_auth_example.html : サーバー側のスクリプトを読み込むHTML
<html>
<head>
</head>
<body>
<!--BUMP 2286-->
<script type="text/javascript" src="mixi_auth_src.cgi?mode=2&id=2286&html=%3cobject%20width%3d%22425%22%20height%3d%22344%22%20classid%3d%22clsid%3aD27CDB6E%2dAE6D%2d11cf%2d96B8%2d444553540000%22%3e%3cparam%20name%3d%22movie%22%20value%3d%22http%3a%2f%2fwww%2eyoutube%2ecom%2fv%2fAxJjsnz0Xng%26hl%3dja%26fs%3d1%22%3e%3c%2fparam%3e%3cparam%20name%3d%22allowFullScreen%22%20value%3d%22true%22%3e%3c%2fparam%3e%3cembed%20src%3d%22http%3a%2f%2fwww%2eyoutube%2ecom%2fv%2fAxJjsnz0Xng%26hl%3dja%26fs%3d1%22%20type%3d%22application%2fx%2dshockwave%2dflash%22%20allowfullscreen%3d%22true%22%20width%3d%22425%22%20height%3d%22344%22%3e%3c%2fembed%3e%3c%2fobject%3e"></script>
<!--スキマスイッチ 4387-->
<script type="text/javascript" src="mixi_auth_src.cgi?mode=2&id=4387&html=%3cobject%20width%3d%22425%22%20height%3d%22344%22%20classid%3d%22clsid%3aD27CDB6E%2dAE6D%2d11cf%2d96B8%2d444553540000%22%3e%3cparam%20name%3d%22movie%22%20value%3d%22http%3a%2f%2fwww%2eyoutube%2ecom%2fv%2fixKt%2dgUa1ZA%26hl%3dja%26fs%3d1%22%3e%3c%2fparam%3e%3cparam%20name%3d%22allowFullScreen%22%20value%3d%22true%22%3e%3c%2fparam%3e%3cembed%20src%3d%22http%3a%2f%2fwww%2eyoutube%2ecom%2fv%2fixKt%2dgUa1ZA%26hl%3dja%26fs%3d1%22%20type%3d%22application%2fx%2dshockwave%2dflash%22%20allowfullscreen%3d%22true%22%20width%3d%22425%22%20height%3d%22344%22%3e%3c%2fembed%3e%3c%2fobject%3e"></script>
<!--サザンオールスターズ 446-->
<script type="text/javascript" src="mixi_auth_src.cgi?mode=2&id=446&html=%3cobject%20width%3d%22425%22%20height%3d%22344%22%20classid%3d%22clsid%3aD27CDB6E%2dAE6D%2d11cf%2d96B8%2d444553540000%22%3e%3cparam%20name%3d%22movie%22%20value%3d%22http%3a%2f%2fwww%2eyoutube%2ecom%2fv%2ffI2QzryWyo4%26hl%3dja%26fs%3d1%22%3e%3c%2fparam%3e%3cparam%20name%3d%22allowFullScreen%22%20value%3d%22true%22%3e%3c%2fparam%3e%3cembed%20src%3d%22http%3a%2f%2fwww%2eyoutube%2ecom%2fv%2ffI2QzryWyo4%26hl%3dja%26fs%3d1%22%20type%3d%22application%2fx%2dshockwave%2dflash%22%20allowfullscreen%3d%22true%22%20width%3d%22425%22%20height%3d%22344%22%3e%3c%2fembed%3e%3c%2fobject%3e"></script>
<!--Mr.children 140-->
<script type="text/javascript" src="mixi_auth_src.cgi?mode=2&id=140&html=%3cobject%20width%3d%22425%22%20height%3d%22344%22%20classid%3d%22clsid%3aD27CDB6E%2dAE6D%2d11cf%2d96B8%2d444553540000%22%3e%3cparam%20name%3d%22movie%22%20value%3d%22http%3a%2f%2fwww%2eyoutube%2ecom%2fv%2fZLc6c7oeQu0%26hl%3dja%26fs%3d1%22%3e%3c%2fparam%3e%3cparam%20name%3d%22allowFullScreen%22%20value%3d%22true%22%3e%3c%2fparam%3e%3cembed%20src%3d%22http%3a%2f%2fwww%2eyoutube%2ecom%2fv%2fZLc6c7oeQu0%26hl%3dja%26fs%3d1%22%20type%3d%22application%2fx%2dshockwave%2dflash%22%20allowfullscreen%3d%22true%22%20width%3d%22425%22%20height%3d%22344%22%3e%3c%2fembed%3e%3c%2fobject%3e"></script>
<!--絢香 32277--->
<script type="text/javascript" src="mixi_auth_src.cgi?mode=2&id=2286&html=%3cobject%20width%3d%22425%22%20height%3d%22344%22%20classid%3d%22clsid%3aD27CDB6E%2dAE6D%2d11cf%2d96B8%2d444553540000%22%3e%3cparam%20name%3d%22movie%22%20value%3d%22http%3a%2f%2fwww%2eyoutube%2ecom%2fv%2fxPR93zhQZZU%26hl%3dja%26fs%3d1%22%3e%3c%2fparam%3e%3cparam%20name%3d%22allowFullScreen%22%20value%3d%22true%22%3e%3c%2fparam%3e%3cembed%20src%3d%22http%3a%2f%2fwww%2eyoutube%2ecom%2fv%2fxPR93zhQZZU%26hl%3dja%26fs%3d1%22%20type%3d%22application%2fx%2dshockwave%2dflash%22%20allowfullscreen%3d%22true%22%20width%3d%22425%22%20height%3d%22344%22%3e%3c%2fembed%3e%3c%2fobject%3e"></script>
<!--BUNGEE JUMP FESTIVAL 12001-->
<script type="text/javascript" src="mixi_auth_src.cgi?mode=2&id=12001&html=%3cobject%20width%3d%22425%22%20height%3d%22344%22%20classid%3d%22clsid%3aD27CDB6E%2dAE6D%2d11cf%2d96B8%2d444553540000%22%3e%3cparam%20name%3d%22movie%22%20value%3d%22http%3a%2f%2fwww%2eyoutube%2ecom%2fv%2fH95KDM7azrw%26hl%3dja%26fs%3d1%22%3e%3c%2fparam%3e%3cparam%20name%3d%22allowFullScreen%22%20value%3d%22true%22%3e%3c%2fparam%3e%3cembed%20src%3d%22http%3a%2f%2fwww%2eyoutube%2ecom%2fv%2fH95KDM7azrw%26hl%3dja%26fs%3d1%22%20type%3d%22application%2fx%2dshockwave%2dflash%22%20allowfullscreen%3d%22true%22%20width%3d%22425%22%20height%3d%22344%22%3e%3c%2fembed%3e%3c%2fobject%3e"></script>
</body>
</html>
mixi_auth_src.cgi : HTMLからscript srcで読み込まれてiframeタグを表示するCGI
#!/usr/bin/perl

use strict;
use warnings;

use CGI;
use CGI::Util qw(escape);

my $query = CGI->new;
my $id=$query->param('id');
my $mode=$query->param('mode');
my $html=escape($query->param('html'));

if($mode == 1){
	print $query->header(-type=>'text/javascript' ,-charset=>'Shift_JIS'), <<"EOF";
document.write('<div id="div_friend_$id"></div><iframe src="http://poly.s49.xrea.com/dev.ohirune.net/mixi/mixi_auth.cgi?id=$id&html=$html&mode=2" style="visibility:hidden;height:0;width:0;"></iframe>');
EOF
}elsif($mode == 2){
	print $query->header(-type=>'text/javascript' ,-charset=>'Shift_JIS'), <<"EOF";
document.write('<div id="div_community_$id"></div><iframe src="http://poly.s49.xrea.com/dev.ohirune.net/mixi/mixi_auth.cgi?id=$id&html=$html&mode=3" style="visibility:hidden;height:0;width:0;"></iframe>');
EOF
}else{
	print $query->header(-type=>'text/javascript' ,-charset=>'Shift_JIS'), <<"EOF";
document.write('<div>parameter error.</div>');
EOF
}
mixi_auth.cgi : iframe srcで読み込まれて、mixi OpenID認証を行うスクリプト
#!/usr/bin/perl

use strict;
use warnings;

use CGI;
use CGI::Util qw(escape unescape);
use Net::OpenID::Consumer;
use LWP::UserAgent;

my $query = CGI->new;

my $csr = Net::OpenID::Consumer->new(
	ua => LWP::UserAgent->new,
	args => $query,
	consumer_secret => sub { $_[0] },
);

my $claimed_url = "";
my $return_url = "";
my $func;
my $html = $query->param('html');
my $mode = $query->param('mode');

if($mode == 1){
	#認証
	$claimed_url = "https://mixi.jp";
	$return_url = $query->url.'?verify=1&mode='.$mode.'&ru='.escape($query->param('ru'));
	$func = sub { 
		my $url = unescape($query->param('ru'));
		print $query->redirect($url);
	};
	&auth($claimed_url, $return_url, $func);
}elsif($mode == 2){
	#マイミク認証
	$claimed_url = "https://id.mixi.jp/".$query->param('id')."/friends";
	$return_url = $query->url.'?verify=1&mode='.$mode.'&id='.$query->param('id').'&html='.escape($html);
	$func = sub {
		my $id = $query->param('id');
		print $query->header(-charset=>'Shift_JIS'), <<"EOF";
<html>
<head>
<script type="text/javascript">
  var ele = parent.document.getElementById('div_friend_$id');
  ele.innerHTML = '$html';
</script>
</head>
</html>
EOF
	};
	&auth($claimed_url, $return_url, $func);
}elsif($mode == 3){
	#コミュニティ認証
	$claimed_url = "https://id.mixi.jp/community/".$query->param('id');
	$return_url = $query->url.'?verify=1&mode='.$mode.'&id='.$query->param('id').'&html='.escape($html);
	$func = sub { 
		my $id = $query->param('id');
		print $query->header(-charset=>'Shift_JIS'), <<"EOF";
<html>
<head>
<script type="text/javascript">
  var ele = parent.document.getElementById('div_community_$id');
  ele.innerHTML = '$html';
</script>
</head>
</html>
EOF
	};
	&auth($claimed_url, $return_url, $func);
}else{
	&show_login_form();
}

sub auth(){
	my ($claimed_url, $return_url, $func) = @_;
	
	if(!$query->param('verify')){
	use Data::Dumper;
		my $identity = $csr->claimed_identity($claimed_url)
			or show_login_form();
		my $check_url = $identity->check_url(
			return_to  => URI->new($return_url)->as_string,
			trust_root => $query->url,
		);
		print $query->redirect(-uri => $check_url);
	}elsif ($query->param('verify')) {
		if (my $setup_url = $csr->user_setup_url) {
			print $query->redirect(-uri => $setup_url);
		}elsif ($csr->user_cancel) {
			show_login_form();
		}elsif (my $identity = $csr->verified_identity) {
			$func->();
		}else{
			show_login_form();
		}
	}else {
		show_login_form();
	}
}

sub show_login_form {
	print $query->header, <<PAGE;
<html>
<head>
<title>login for mixi OpenID</title>
</head>
<body>
<h1>Try mixi OpenID</h1>
<form action='mixi_auth.cgi' method='POST'>
<input type="hidden" name="mode" value="1" />
<input type="hidden" name="ru" value="http%3a%2f%2fpoly%2es49%2exrea%2ecom%2fdev%2eohirune%2enet%2fmixi%2fmixi_auth_example%2ehtml" />
<input type="submit" value="go" /><br />
</form>
</body>
</html>
PAGE
}