Catalystでmixi OpenIDからニックネームを取得

ボヤボヤしてるうちにmixiOpenIDのProviderになって、そろそろOpenIDのRPに対応する準備しとかんとあかんやろ、と思い、認証してニックネームを取得してくるだけのRPのテストプログラムを作ってみた。
ここで紹介されているサンプルのCatalyst版(の更にマイミク認証を除いたやつ)です。

Catalystは当然インストールしてあるとして、事前にNet::OpenID::ConsumerとCatalyst::Authentication::Credential::OpenIDの2つのモジュールをCPANに上がっているやつじゃなくてSubversionリポジトリから取得してそれぞれインストールします。
Net::OpenID::Consumerの方は依存モジュールに注意した方が良いみたいなので、以下のページを参考に先に他のモジュールや外部ライブラリをインストールしておく。

$ svn co http://code.sixapart.com/svn/openid/trunk/perl/Net-OpenID-Consumer/
$ perl Makefile.PL
$ make
$ make install
$ svn co http://svn.coderepos.org/share/lang/perl/Catalyst-Authentication-Credential-OpenID/trunk/ Catalyst-Authentic
ation-Credential-OpenID
$ perl Makefile.PL
$ make
$ make install

自分の環境ではもう一つハマリどころがあって、Net::OpenID::Consumer(正確に言うと同梱されているNet::OpenID::Yadis)がXML::Simpleを使用しているんだけどMakefileのPREREQ_PMに書いてないのでインストール要求されなかったこと。呼び出し側のCatalyst::Authentication::Credential::OpenIDの方でコンパイルエラーが起きていたので原因を特定するのに時間がかかってしまった。なので、手元の環境にXML::Simpleを入れてない人は別途インストールする必要があります。

この2つのモジュールを入れてしまえば、あとはcatalyst.pl MyAppしてMyApp.ymlとMyApp.pmとRoot.pmを以下の例の様に書き換えればOK。

MyApp.yml

---
name: MyApp

Log::Dispatch:
  - class: File
    name:  file
    filename: __path_to(test.log)__
    mode: append
    min_level: debug
    format: '%P [%p] %m %n'
  - class: Screen
    name:  screen
    min_level: debug
    stderr: 1
    format: '[%p] %m%n'

Plugin::Authentication:
    default_realm: openid
    realms:
        openid:
            credential:
                class: OpenID
                extension_args:
                    'http://openid.net/extensions/sreg/1.1':
                        required: nickname

MyApp.pm

package MyApp;

use strict;
use warnings;

use Catalyst::Runtime '5.70';

use Catalyst qw(
    ConfigLoader
    Static::Simple
    Log::Dispatch
    Authentication
    Session
    Session::Store::FastMmap
    Session::State::Cookie
);

our $VERSION = '0.01';

__PACKAGE__->config( name => 'MyApp' );

# Start the application
__PACKAGE__->setup;

1;

Root.pm

package MyApp::Controller::Root;

use strict;
use warnings;
use base 'Catalyst::Controller';

__PACKAGE__->config->{namespace} = '';

sub default : Private {
    my ($self, $c) = @_;
    $c->log->debug('### default start');

    my $template = << "TEMPLATE"
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>mixi OpenID Login Test</title>
</head>
<body>

<h1>mixi OpenID Login Test</h1>

ボタンをクリック!<a href="/login?openid_identifier=https://mixi.jp"><img src="/static/images/b_150.gif" alt="mixiでログイン" border="0"></a>
TEMPLATE
;

    $c->res->body($template);
    $c->log->debug('### default end');
}

sub login : Local {
    my ($self, $c) = @_;
    $c->log->debug('### login start');

    if ($c->authenticate) {
        $c->log->debug('### authenticated!');
        # success
        my $id = $c->user->{url};
        my $nickname = $c->user->{display};
        my $template = << "TEMPLATE"
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>mixi OpenID Login Test SUCCESS!</title>
</head>
<body>

<h1>mixi OpenID Login Test</h1>

ログインに成功しました!<br>
こんにちは、$nickname ( $id ) さん

TEMPLATE
;
        $c->res->body($template);
    }
    elsif ($c->res->redirect) {
        $c->log->debug('### redirect');
        # redirect to OP/IdP, nothing to do
    }
    else {
        # error
        $c->log->debug('### error!');
        my $template = << "TEMPLATE"
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>mixi OpenID Login Test ERROR!</title>
</head>
<body>

<h1>mixi OpenID Login Test</h1>

ログインに失敗しました。

TEMPLATE
;
        $c->res->status(401);
        $c->res->body($template);
    }
    $c->log->debug('### login end');
}

sub end : ActionClass('RenderView') {}

1;


認証成功後は、$c->user->{url}でユーザのIdentifierが、(extention_argsにsregでnicknameを要求していれば)$c->user-{display}にニックネームが取得できます。今回はmixiからニックネームを持ってこれるか試したかったので手抜きでhttps://mixi.jpを直書きしたけど、Claimed Identifier(Root.pmのopenid_identifierの部分)を入力できるようにすれば他のOpenID Providerにも対応できるはず。試してないけど。