連載の第1回では既存サービスを例にして、OP(OpenID Provider)が提供するOpenIDアカウントでRP(Relying Party)へのログインを試してみました。
今回からは、自分でOpenIDに対応したRPを動かしながら、OpenIDの使い方を説明していきます。
第2回ではOpenIDに対応したライブラリを紹介し、Ruby用のライブラリを使ってRPのサンプルを動作させてみましょう。
OpenIDの対応ライブラリ
OpenIDアカウントでログイン可能なサイト(RP)を作るためには、OpenIDの仕様で定められたプロトコルに準拠しなければいけません。
幸い、 OpenIDの仕様に対応した各言語のライブラリがオープンソースとして公開されています。これらのライブラリを使うことで、OpenIDの細かなプロトコルを自分でプログラミングしなくて良いようになっています。
ここでは、いくつかのOpenID対応ライブラリをご紹介します。
OpenID Enabledのライブラリ
openidenabled.com というサイトにて、JanRain, Inc.が実装したOpenIDライブラリが公開されています。
対応言語はPHP、Python、Rubyの3種類です。いずれもOpenIDのバージョン2に対応しています。JanRain, Inc.はmyOpenID というOPも運営しています。
code.sixapart.comのライブラリ
PerlでのOpenIDライブラリは、Six Apart Code リポジトリにて公開されているモジュールに、limilic.com で公開されているパッチを当てたものが使われているようです。
詳しくはYappoLogsのエントリ が参考になります。
それでは、Ruby用のOpenIDに付属しているRailsのサンプルを動かしてみましょう。
RailsでOpenID対応のRPを動かしてみよう
こんどは、自前のRPサーバを動かしてみましょう。Rubyで使えるOpenIDライブラリとして、ruby-openidというものがあります。ruby-openidにはRailsのサンプルも付属しているので、それを使用します。筆者はMac OS X上で動かしましたが、Railsが使える環境であれば他のOSでも大丈夫です。
ここでは、以下の環境を想定しています。すでにRailsを動かしているのであれば、RubyGemsもインストールされているでしょう。
Ruby1.8.6
RubyGems1.0.2
Rails2.0.2
OpenIDライブラリのインストール
Ruby用のOpenIDライブラリであるruby-openidのバージョンは2.0.4です。このライブラリはOpenID Authentication 2.0に対応しています。
ruby-openidは以下のようにRubyGemsを使ってインストールします。
$ sudo gem install ruby-openid
正しくインストールされたかどうかは、listコマンドで確認できます。
$ gem list
*** LOCAL GEMS ***
(中略)
rails (2.0.2)
rake (0.8.1)
ruby-openid (2.0.4)
RubyGemsでruby-openidをインストールすると、ライブラリの一式は下記のディレクトリにコピーされます。
ここで、${RUBYLIB}はRubyライブラリのインストール先に読み替えてください。
一般的には/usr/lib/rubyや/usr/local/lib/rubyが相当します。
${RUBYLIB}/gems/1.8/gems/ruby-openid-2.0.4
このディレクトリには、ライブラリ本体以外にサンプルやテストも同時にインストールされます。
READMEやCHANGELOGに目を通されておくといいでしょう。
Railsサンプルの起動
ruby-openidライブラリには、Railsを使ったOPとRPのサンプルが含まれています。
これらのサンプルはライブラリインストール先のexample/rails-openidにあります。
まず、このディレクトリをどこか適当な場所にコピーしてください。
$ cp -r example/rails-openid $HOME/openid
$ cd $HOME/openid/rails-openid
Railsを動かすために、必要なlog,dbディレクトリを作成します。
$ mkdir log db
なお、ruby-openidに付属するRailsのサンプルでは、Rails2.0を動作させるために必要なセッションCookieの設定が書かれていません。そのため、設定ファイルにセッションの設定を追記する必要があります。下記のように:session_key, :secretを設定してください。
secretは30文字以降のランダムな文字列です。
$ vi config/environment.rb
config.action_controller.session = {
:session_key => '_openid_session',
:secret => 'c67504f48.....7e2557bc0'
}
準備ができたらRailsサーバを起動します。
ruby script/server
ブラウザでhttp://サーバのアドレス:3000/consumerにアクセスしてログイン画面が表示されれば起動成功です。
OP への認証リクエストの送信
ここからはサンプルを動かしながら、ruby-openidライブラリの簡単な使い方について説明します。
Railsを動かす環境が無い方は、ruby-openidライブラリのサイト で同じサンプルが公開されていますので、こちらにアクセスしてみるのがよいでしょう。サイトの一番下にある、「 Live Demos」の「2.x.x trunk demo relying party」からRPのサンプルへアクセスできます。
ログイン画面の表示(index.rhtml)
ログイン画面のソースコードはapp/views/consumer/index.rhtmlに記述されています。
以下にソースコードの一部を抜粋します。
69 <form method="get" accept-charset="UTF-8"
70 action='<%= url_for :action => 'start' %>'>
71 Identifier:
72 <input type="text" class="openid" name="openid_identifier" />
73 <input type="submit" value="Verify" /><br />
Identifierと書かれた入力フィールドがOpenIDのアカウント名(User-Supplied URL)を入力する箇所です。
OPへのリダイレクト(ConsumerController#start)
Identifierに自分のOpenIDを入力してVerifyボタンを押すと、Consumerアクションのstartメソッドが呼ばれます。Consumerアクションはapp/controllers/consumer_controller.rbです。
この中のstartメソッドから重要な箇所だけを説明します。
まず、ユーザが入力したOpenIDのURLを引数に beginメソッドを呼び出して、OpenIDの認証要求を生成してもらいます。
consumerはruby-openidが提供するOpenID::Consumerクラスのオブジェクトです。
OpenID::Consumerは引数として渡されたURLからOpenIDのOPを見つけ出します。
戻り値のoidreqは認証要求を扱うためのOpenID::Requestクラスのオブジェクトです。
# ユーザが入力したopenid_identifierを元にOpenIDリクエストを作成
17 oidreq = consumer.begin(params[:openid_identifier])
oidreqのredirect_urlメソッドを呼び出して認証サーバ(OP)のログイン画面URLを取得し、そのURLへ利用者をリダイレクトします。
# 利用者をOPのログイン画面(OP EndPoint)へと誘導する
47 redirect_to oidreq.redirect_url(realm, return_to, params[:immediate])
なお、redirect_urlメソッドの引数として渡しているreturn_toとrealmパラメータは、直前で下記のように生成しています。
# 認証サーバ(OP)からの戻り先URL(completeアクションのURL)
43 return_to = url_for :action => 'complete', :only_path => false
# このサーバを識別するためのrealm
44 realm = url_for :action => 'index', :only_path => false
これで、ユーザはOPのログイン画面へと転送されます。
OPでのログイン
OPのログイン画面で、IDとパスワードを入力してログインします。
先ほどのはてなやYahoo!の例と同じですので、説明は割愛します。
OPからの認証結果の受け取り (ConsumerController#complete)
OPからの認証結果は、リクエストで指定したreturn_toへと戻ってきます。
今回はcompleteアクションを指定したので、completeメソッドが呼ばれます。
以下にソースコードを抜粋します。
55 current_url = url_for(:action => 'complete', :only_path => false)
56 parameters = params.reject{|k,v|request.path_parameters[k]}
# 戻り値を解析し、OpenID::Responseクラスのオブジェクトを作成
57 oidresp = consumer.complete(parameters, current_url)
completeメソッドの戻り値のoidrespは、OpenID::Responseクラスのオブジェクトです。
OPでのログインに成功したかどうかは、oidresp.statusで確認できます。
statusは下記4つの値をとります。
OpenID::Consumer::FAILURE…メッセージの検証に失敗した
OpenID::Consumer::SUCCESS…ログインに成功した
OpenID::Consumer::SETUP_NEEDED…セットアップが必要
OpenID::Consumer::CANCEL…ユーザがログインを拒否した
ログインに成功していれば(statusがSUCCESSであれば) 、OpenID::Responseオブジェクトのdisplay_identifierからユーザのOpenIDアカウントを取得できます。
サンプルでは、画面に表示するために文字列として格納していますが、実際のアプリケーションでは、取得したOpenIDアカウント名をセッションに格納する処理が入ることになるでしょう。
# OpenIDレスポンスのステータスを確認
57 case oidresp.status
# ログイン成功なら、display_identifierからOpenIDのアカウント名を取得
65 when OpenID::Consumer::SUCCESS
66 flash[:success] = ("Verification of #{oidresp.display_identifier}"¥
67 " succeeded.")
最後に、取得したIDを画面に表示しています。
いかがでしたでしょうか。
少し概念が難しく感じるかもしれませんが、この程度のサンプルであれば意外と簡単に作れることが分かったのではないでしょうか。
はてなやYahoo!のような多くのユーザを抱えるサイトがOpenIDのOPサービスを提供し、OpenIDを使うためのライブラリが充実している今、OpenIDは意外と身近なものになっているのかもしれません。
次回は、OpenIDライブラリを使いながら、利用者が入力したOpenIDアカウント名からOPの認証画面のURLを見つけるdiscoveryの仕組みや、OpenIDの拡張領域を使ってOPに登録されている利用者のメールアドレスを取得する方法などを説明します。