OpenID 2.0とCatalystで遊んでみる

まず各所に転がっているOpenID2.0対応版ライブラリに差し替える。

ここまで入れたら、適当にcatalyst.plして、

sub openid : Local {
        my ( $self, $c ) = @_;

        if ( $c->authenticate() ) {
                $c->flash( message => 'You signed in with OpenID!' );
                $c->res->redirect( $c->uri_for('/') );
        } else {
                #present OpenID form.
        }
}

とかRoot.pmに書いておいて、

Plugin::Authentication:
  default_realm: openid
  realms:
    openid:
      credential:
        class: OpenID
      store:
        class: 'DBIx::Class'
        user_class: 'DB::OpenidIds'
        id_field: 'user_id'
      ua_class: LWPx::ParanoidAgent
      extension_args:
        "http://openid.net/extensions/sreg/1.1":
          required: email,nickname

設定ファイルにこんな感じで書いて、DBにそれっぽい感じで入れておくと、DBに入れておいたIDまでひもづけて持ってきてくれるようになった。

Session
{
  __created    => 1221244640,
  __updated    => 1221247804,
  __user       => { display => "XXX", id => 2, url => "https://id.mixi.jp/000", user_id => 2 },
  __user_realm => "openid",
}

……とここまで書いて、sreg.nicknameを使えていないことに気がついた。store使わなければ__userに全フィールド残ってるんだけどどうすればよいだろう。Catalyst/Authentication/Store/DBIx/Class/User.pm の load() では見つかったらauthinfo捨ててるみたいだしなあ。DBに同名カラムがなかったりignorefieldsに指定されているような属性値を保存したい場合はどうすれば良いんだろう。

もしかしたら auto_update とやらを使えばよい?
あたり。スキーマクラスにauto_update定義してあげればDB側を更新できるようです。

sub auto_update {
    my( $self, $userinfo, $req, $res ) = @_;

    foreach my $key (keys %{$userinfo}) {
        if ($self->has_column($key)) {
            $self->set_column( $key => $userinfo->{$key} );
        }
    }

    $self->update;
}

これでDB側にカラムがあるものは更新。ただし、放っておくと検索にも使われてしまうので、設定ファイル側でそのカラムは無視指定をしておく必要があります。

      store:
        class: 'DBIx::Class'
        user_class: 'DB::OpenidIds'
        id_field: 'user_id'
        ignore_fields_in_find: [ 'display' ]

これでようやく、OpenIDとDB内部のユーザとのひも付けができるようになりました。

同様にauto_createを用意してあげれば、アカウントがまだ存在しない場合に作ってしまうこともできそうですね。