Railsコードリーディング

第5回 OpenIDを実装したソースコードを読もう

倉貫 義人
松村 章弘
TIS株式会社
SonicGarden

2009/6/3

icon RPとして動作するSKIPのコードを読む

 OPとRPの実装を比べると、OpenIDを利用するだけのRPの方が比較的容易です。まずは、RPとして動作する場合のSKIPのソースコードを見ていきましょう。

 RailsアプリケーションをRPにしたい場合、そのためのopen_id_authenticationというプラグインが存在しています。SKIPでもこのプラグインを採用しており、SKIPプロジェクト内のvender/plugins/open_id_authenticationというディレクトリ内に配置されています。このプラグインを活用したRPの実装を説明します。

 実際に動いているSKIPの動作を基に説明をしていきます。SKIPをRPとして動作させている実例として、SKIPユーザグループ(SUG)のコミュニティサイトがあります。

関連リンク:
リンク SKIPユーザグループ
http://user.openskip.org/

 SUGは、SKIPの利用者や開発者同士の情報交換をするためのSNS(SKIP)になっています。オープンソースなので当然ですが、どなたでも無料で登録して参加できますので、RPの動作を試せます(前回まで使ったSKIPデモサイトは、RPとしては動作していません)。

 SUGへのログインまでの動作の流れを順に確認した上で、その流れをソースコードで見ていきます。今回は例として、SUGへのログインをするためにmixiをOpenIDの認証局として利用します。実際の動作の流れは以下のようになります。

  1. SUGにアクセスし、トップページにある「mixi IDでログイン」をクリックする
  2. mixiのログイン画面に遷移するので、ログインを行い、SUGへの認証情報の受け渡し許可を行います
  3. ログインが成功するとSUGでは認証済みの状態になり、プロフィール登録やマイページが表示されます

 SUG自体は認証情報を持ちませんが、mixiの認証処理でログインできるようになっています。この処理の流れをソースコードで追っていきましょう。

 1つ目の「mixi IDでログイン」のリンクは、以下のURLになっています。

http://user.openskip.org/login?openid_url=mixi.jp

 このURLが、OpenIDでの認証の出発点になります。このURLにアクセスした際に実行されるソースコードは以下のメソッドです。

app/controllers/platform_controller.rb
45
46
47
48
49
50
51
def login
  if using_open_id?
    login_with_open_id
  else
    login_with_password
  end
end

 46行目のusing_open_id?メソッドは、open_id_authenticationプラグインが提供しているメソッドで、OpenID関連のパラメータが正しいものかどうかを確認します。今回は、パラメータにopened_urlを含んでいるので、このusing_open_id?メソッドはtrueを返します。

 次に、47行目でlogin_with_open_idメソッドを呼び出しているので、そちらを見てみましょう。

app/controllers/platform_controller.rb
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
def login_with_open_id
  session[:return_to] = params[:return_to] if !params[:return_to].blank? and params[:open_id_complete].blank?
  begin
    authenticate_with_open_id( params[:openid_url],
                               :required => INITIAL_SETTINGS["ax_fetchrequest"].values.flatten) do |result, identity_url, registration|
      if result.successful?
        logger.info("[Login successful with OpenId] \"OpenId\" => #{identity_url}")
        unless identifier = OpenidIdentifier.find_by_url(identity_url)
          create_user_from(identity_url, registration)
        else
          return_to = session[:return_to]
          reset_session

          self.current_user = identifier.user_with_unused
          redirect_to_return_to_or_root(return_to)
        end
      else
        logger.info("[Login failed with OpenId] \"OpenId\" => #{identity_url}")
        set_error_message_form_result_and_redirect(result)
      end
    end
  rescue OpenIdAuthentication::InvalidOpenId
    logger.info("[Login failed with OpenId] \"OpenId is invalid\"")
    flash[:error] = _("OpenIDの形式が正しくありません。")
    redirect_to :action => :index
  end
end

 login_with_open_idメソッドのポイントは、218行目のauthenticate_with_open_idです。このメソッドも、open_id_authenticationプラグインで提供されているものです。

 OpenID認証を開始する場合、このメソッドの中ではパラメータのopened_urlの情報を使って、指定されたOP(ここではmixi)にリダイレクト処理を行い、OpenID認証の処理が開始されます。ここまででSKIP側の処理は一時中断されて、OPでの認証を待ちます。

 2つ目の処理は、mixi側での処理になります。OpenID Providerの仕様に則って実装されているので、Yahoo!などほかのOPを使った場合も外から見た動きは同じになります。mixiでのログイン処理が正常に終了すると、SUGにリダイレクトされます。

 3つ目の処理は、mixiからリダイレクトされたSUG側の処理になります。SUGの戻り先のURLは、先ほどと同じhttp://user.openskip.org/loginですが、OPであるmixiからの適切な認証情報がパラメータとして付与されています。

 再び、loginメソッド内のusing_open_id?で判定を行い、正しいOPからの戻りであることを確認した後、改めてlogin_with_open_idが呼ばれます。1つ目の処理のときとは違い、OPからの適切なパラメータが付与されていることをauthenticate_with_open_idが確認できれば、続く220行目から234行目の実行に移ります。

 この後は、Webアプリケーション独自の処理になります。SKIPの場合は、利用開始済みのユーザーか未開始ユーザーかで判別して(222行目)、その後の処理を振り分けています。

 これが、RPとして動作するためのソースコードの流れになります。open_id_authenticationプラグインを使うことで、OPとRPとの鍵共有のやりとりなどの複雑な部分はかなり隠蔽された状態でRPを実装できることが分かると思います。

icon SKIPにおけるRPの活用シーンと独自の拡張

 SUGでのRPの使い方は非常にポピュラーなものなので、一般的なWebアプリケーションであれば、そのまま応用して使うことができると思います。

 社内SNSであるSKIPのRPとしての主な利用シーンは、すでにある社内の社員情報を活用したい場合です。

 例えば、社員情報がLDAPでアクセスできるようになっているとしたら、SKIPを導入する際に新たにユーザー情報を登録し直すのは手間ですし、ユーザーにとってもパスワードが変わってしまうと不便です。

 このような場合に、社員情報にアクセスできる独自のOpenID Provider相当のアプリケーションを用意することで、SKIP側で認証情報を管理しなくて済むようになります。

図3 社内情報を活用してSKIPをRPで動作させる

 このような用途の場合、認証を許可するOPは固定されなければなりません。SKIPでは、SUGのようにどのOPでも許可するモードと、指定のOPでの認証だけを許可するモードが選べるような独自拡張をしています。

 指定OPだけにした場合、SKIPの利用者はOPを選択する必要がなくなりますので、擬似的にシングルサインオンのような形を実現できます。このことは、必ずしもOpenIDのような認証の仕組みに慣れていないユーザーの多い社内利用でのメリットになります。

 擬似的なシングルサインオンを実現するために独自に拡張している部分のソースコードは以下になります。

app/controllers/application.rb
34
 
312
313
314
315
316
317
318
319
320
321
322
before_filter :sso, :login_required, :prepare_session
(中略)
def sso
  if login_mode?(:fixed_rp) and !logged_in?
    if request.env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'
      rentder :text => _('ログインが必要です。ログインし直して下さい。'), :status => :bad_request
    else
      redirect_to :controller => '/platform', :action => :login, :openid_url => INITIAL_SETTINGS['fixed_op_url'], :return_to => URI.encode(request.url)
    end
    return false
  end
  true
end

 このようにApplicationControllerで、ほぼすべての処理のbefore_filterで指定したSSOメソッドで、固定OPでのログイン処理にリダイレクトする(317行目)ことで、ユーザーにOPのURLを入力させる必要なく固定のOPへのログイン処理を開始するという仕組みになっています。

next
2/3
next

Index
OpenIDを実装したソースコードを読もう
  Page1
OpenIDでWebの認証を便利に実現
OpenIDについて
SKIPバージョン1.1リリースしました!
Page2
RPとして動作するSKIPのコードを読む
SKIPにおけるRPの活用シーンと独自の拡張
  Page3
OPとして動作するSKIPのコードを読む

index Railsコードリーディング 〜scaffoldのその先へ〜

 Ruby/Rails関連記事
プログラミングは人生だ
まつもと ゆきひろのコーディング天国
 ときにプログラミングはスポーツであり、ときにプログラミングは創造である。楽しいプログラミングは人生をより実りあるものにしてくれる
生産性を向上させるRuby向け統合開発環境カタログ
Ruby on Rails 2.0も強力サポート
 生産性が高いと評判のプログラミング言語「Ruby」。統合開発環境を整えることで、さらに効率的なプログラミングが可能になる
かんたんAjax開発をするためのRailsの基礎知識
Ruby on RailsのRJSでかんたんAjax開発(前編)
 実はAjaxアプリケーション開発はあなたが思うよりも簡単です。まずはRuby on Railsの基礎知識から学びましょう
Praggerとnetpbmで作る画像→AA変換ツール
Rubyを使って何か面白いものを作ってみよう!
 一般的な画像をアスキーアートに変換するツールを作ってみる。さらに出力にバリエーションを持たせてみよう
コードリーディングを始めよう
Railsコードリーディング〜scaffoldのその先へ〜(1)
 優れたプログラマはコードを書くのと同じくらい、読みこなす。優れたコードを読むことで自身のスキルも上達するのだ
  Coding Edgeフォーラムフィード  2.01.00.91


Coding Edge フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

>

Coding Edge 記事ランキング

本日 月間