はじめに
Devise を使って実装した認証機能を、Facebook の OAuth を使ったものに変更したい。そこで OmniAuth と OminiAuth Facebook を導入して、認証機能を修正してみた。
OmniAuth の Wiki 通りにやれば簡単にいくと思っていたら、ちょっと躓いたので、手順をメモしておく。
ちなみに、参考にした Wiki はこちら。
Facebook API キーとシークレットキーを取得
Facebook Developers にログインして、アプリを登録する。
アプリを登録したら、アプリ一覧で API キーとシークレットキーを確認できる。
登録する際、「Facebookでログインするウェブサイト」にひとまず次の URL を設定しておく。
http://localhost:3000
これを設定していなかったら、開発環境でデバッグしたとき、Facebook でログインしようとしてリダイレクトエラーになってしまった。
アプリ公開時は、正式なアプリの URL に変更しておく。
OmniAuth と OmniAuth Facebook をインストール
Gemfile に
gem "omniauth" gem "omniauth-facebook"
を追加して
bundle
を実行。
users テーブルに Facebook 用の列を追加
Devise で認証機能を実装したとき、既に users テーブルを作成していたので、Facebook で認証するのに必要な列を追加するマイグレーションファイルを作成する。
bundle exec rails g migration AddFacebookFieldsToUsers name:string uid:integer provider:string access_token:string
マイグレーション実行。
bundle exec rake db:migrate
User クラスを修正。
Facebook 認証後、データベースにユーザー情報が登録されていなければ登録する。Graph API を使いたいので、アクセストークンも保存しておく。
class User < ActiveRecord::Base # omniauthable を追加し、使わないものは削除。 devise :database_authenticatable, :trackable, :omniauthable # name, provider, uid を追加 attr_accessible :email, :password, :name, :provider, :uid, :access_token def self.find_for_facebook_oauth(auth, signed_in_resource=nil) user = User.where(:provider => auth.provider, :uid => auth.uid).first unless user user = User.create({ :name => auth.extra.raw_info.name, :provider => auth.provider, :uid => auth.uid, :email => auth.info.email, :access_token => auth.credentials.token, :password => Devise.friendly_token[0, 20], }) end user end def self.new_with_session(params, session) super.tap do |user| if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"] user.email = data["email"] if user.email.blank? end end end end
Devise の設定
config/initialzers/devise.rb を編集する。
Devise.setup do |config| # ... config.omniauth :facebook, "APPKEY", "APPSECRET" # ... end
APPKEY と APPSECRET は、Facebook Developers でアプリを登録したとき発行されたものに変更すること。
コントローラを作成
app/controllers/users/omniauth_callbacks_controller.rb を作成。
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def facebook @user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user) if @user.persisted? flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook" sign_in_and_redirect @user, :event => :authentication else session["devise.facebook_data"] = request.env["omniauth.auth"] redirect_to new_user_registration_url end end end
ルーティングの設定
config/routes.rb の devise_for を使っている箇所を修正。
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
最後にビューを編集
app/views/layouts/application.html.erb にサインイン&サインアウトのリンクを追加。
<ul class="nav pull-right"> <% if user_signed_in? %> <li><%= link_to "Sign out", destroy_user_session_path, :method => :delete %></li> <% else %> <li><%= link_to "Sign in", user_omniauth_authorize_path(:facebook) %></li> <% end %> </ul>