みのわーるど::Blog

茨城のシェアハウスの中にある会社で働くITベンチャー役員の奮闘記。

Rails3 + omniauthでログイン処理をTwitterやFacebookに丸投げする

ログイン機能は当然ながら、Webサービスで必須の機能の1つです。しかし、自前で認証機能を持つと面倒だったり、ユーザに対して壁を与えてしまう場合があるかもしれません。そんな時は、omniauthを使ってTwitter, Facebook認証を実装すると良いと思います。いわゆる「Twitterでログイン」ボタンみたいなやつです。

omniauthによって、例えばTwitterで認証を行いたいときは、http://yourapp.com/auth/twittterにリンクすればtwitter認証ページに飛び、認証が成功すればhttp://yourapp.com/auth/twitter/callbackがコールバックされます。規約の力ってすごいですね!

尚、対象とするRailsのバージョンは3です。

1. omniauthのインストール

Gemfileの末尾に、

gem "omniauth"

と記述して、

bundle install

するだけです。Rails3になってから大変便利になりました。

2. omniauthの設定

Twitter及びFacebookのアプリケーション登録画面で作成しているアプリケーションを登録します。

Sign in with your Twitter account | Twitter Developers
Facebook

アプリケーションの名前や詳細は適当で構いませんが、

TwitterのCallback URL http://localhost:3000/auth/twitter/callback
FacebookのサイトURL http://localhost:3000/

にしておきます。本番環境とかでは、当然ながら適宜変更します。どちらも、アプリの作成が済むと、

twitter Consumer Key, Consumer Secret
facebook App ID, App Secret

の値が手に入ると思います。これはとても大切なので控えておいてください。ちなみに私はEvernoteに取り込んでしまっています。そしたら、Railsアプリケーションに戻って、initializers/omniauth.rbというファイルを作成し、以下を書き込みます。

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET'
  provider :facebook, 'App ID', 'App Secret', {:client_options => {:ssl => {:ca_path => "/etc/ssl/certs"}}}
end

これでomniauthの設定は終了です。なんかFacebookだと、SSL証明書の位置を明示しなければいけないみたいです...

ruby on rails - OmniAuth & Facebook: certificate verify failed - Stack Overflow

3. Model, Controllerの作成と設定

まずUserモデルを作成します。Twitterだと返却値の中にメールアドレスが無いので、ユーザに入力して貰う必要があります。今回は本当に最低限ということで放置します...

rails g model user nickname:string email:string image_url:string
rake db:migrate

これで、userテーブルとそれを取り扱うためのUserモデルが作成されてしまいました。速い!

次に、ログイン処理を扱うsessionsコントローラを作成します。ついでに、サイトのトップになるwelcomeコントローラとindexアクションも作成しておきます。

rails g controller welcome index
rails g controller sessions

ここでルーティングを決めておきます。config/routes.rbに以下を記述します。

root :to => 'welcome#index'

# for omniauth settings
match 'auth/:provider/callback' => 'sessions#login'
match '/logout' => 'sessions#logout', :as => :logout

アプリケーションのルートにアクセスしたらwelcome#indexが呼び出されるようにし、またコールバックされたら、sessions#loginが処理を担当します。あと、ログアウト機能はsessions#logoutが担当します。

上で担当アクションを決めたので、処理を実装してしまいます。app/controllers/sessions_controller.rbに以下を記述します。

def login
    auth = request.env['omniauth.auth']
    user = User.find_by_nickname(auth['user_info']['nickname']) || User.create_account(auth)

    session[:user_id] = user.id
    logger.debug auth.to_yaml
    redirect_to root_url, :notice => 'ログインしました。'
  end

  def logout
    session[:user_id] = nil
    redirect_to root_url, :notice => 'ログアウトしました。'
  end

返却値のハッシュをauth変数が受け取り、次に認証したユーザが既に登録しているユーザかを確認しています。まだ未登録のユーザであれば、Userクラスのクラスメソッドcreate_accountを用意し、ユーザ登録処理をここに実装するようにします。本来は、新規登録フォームでユーザに色々情報を登録してもらうという流れになるかと思います。

早速、create_accountメソットを以下の通り実装してしまいましょう。

def self.create_account(auth)
    user = User.new
    user.nickname = auth['user_info']['nickname']
    user.email = auth['user_info']['email']
    user.image_url = auth['user_info']['image']
    user.save!
end

幸いにも、TwitterとFacebookの返却値が似通っているのでコードが少なくて住みました。saveメソッドですが、「!」を付けないと返り値がtrue/falseになりますが、付けるととuserオブジェクトか例外になるようです。

4. View開発

最後にログイン・ログアウトリンクを実装してします。ログイン処理は、ページ全体の共通の処理なので、レイアウトファイルに記述します。app/views/layouts/application.html.erbのbodyの直下あたりに以下を記述します。

<% if current_user -%>
  ようこそ、<%= current_user.nickname %>さん。 | <%= link_to 'logout', logout_path %>
<% else -%>
  <%= link_to 'Twitterでログイン', 'auth/twitter' %>
  <%= link_to 'Facebookでログイン', 'auth/facebook' %>
<% end -%>

ここで、current_userというメソッドを自作しています。view内に長ったらしいrubyのコードがあると、見通しが悪くなってしまう為です。そのためにRailsはviewヘルパーを用意してくれています。今回、ログインユーザがいればuserオブジェクトを返せば良いので、app/helpers/application_helper.rbに

def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
end

と記述します。

5. デモ!

さて、確認してみます。まずサーバを起動してhttp://localhost:3000/にアクセスすると、以下のようなページが出現します。

f:id:yminowa:20130409230834p:plain

そして、TwitterやFacebookでログインボタンを押すと、それぞれ以下のようなページが出現して、そちら側で認証を要求されます。

Twitter

f:id:yminowa:20130409230905p:plain

Facebook

f:id:yminowa:20130409230931p:plain

そして、ログインすると、以下のように表示されます。

f:id:yminowa:20130409231001p:plain

上出来ですね!個人のWebサービス、どんどん盛り上げていきたいものです。もしも動かないとかご意見があればコメントでもTwitterでもお問い合わせください!