##はじめに
会員登録画面を簡単に作ることの出来るRailsのdevise gemというものがあります。
このdevise gemを使ってfacebook認証を可能にするomniauthという機能があるらしいのでそれについて調べてみました。
以下はdeviseのwiki, facebook exampleの和訳です
devise gem についてはこちら
##使い方
まずはomniauth gem を追加してください。Gemfileに以下を記述
gem 'omniauth-facebook'
ここではfacebookの例を示しますが、他のomniauth gemでも同じように使うことが出来ます。一般的にgemの名前は "omniauth-provider"で、porviderのところにfacebook とか twitterとかに置き換えます。
omniauth List
Userモデルにproviderとuidというカラムを追加します
rails g migration AddColumnsToUsers provider uid
rake db:migrate
次にconfig/initializers/devise.rbにproviderの宣言とrequireを書く必要があります
require "omniauth-facebook"
config.omniauth :facebook, "APP_ID", "APP_SECRET"
requestのpermissionを変えるのなら"omniauth-* "のgemのread meを読んでください。
何らかの理由によりDeviseが starategy classを読み込まない場合ははっきりと:strategy_classを明記することが出来ます。
config.omniauth :facebook, "APP_ID", "APP_SECRET", :strategy_class => OmniAuth::Strategies::Facebook
もしこのようなエラーが出たら
OpenSSL::SSL::SSLError (SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed):
必要なomniauthの証明キーがどこにあるのか明記する必要があります。certified gemかメソッドを使ってください(これはあなたの使っているOSに依存します)
config.omniauth :facebook, "APP_ID", "APP_SECRET",
:client_options => {:ssl => {:ca_path => '/etc/ssl/certs'}}
もしこのappをherokuでデプロイしたいのならこのような設定sectionを各必要があります
config.omniauth :facebook, "APP_ID", "APP_SECRET",
{:scope => 'email, offline_access', :client_options => {:ssl => {:ca_file => '/usr/lib/ssl/certs/ca-certificates.crt'}}}
Engin Yard Cloud serverは/etc/ssl/certs/ca-certificates.crtににあります.
OSXの開発においては証明書がfileではなくkeychainに置いてあるので簡単に証明書を無効にすることが出来ます。
require "omniauth-facebook"
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE if Rails.env.development?
config.omniauth :facebook, "APP_ID", "APP_SECRET"
errorについての深い議論は: https://github.com/intridea/omniauth/issues/260 でされています
strategyの設定をした後、model (e.g. app/models/user.r)をomniauthをつけることが必要です。
devise :omniauthable, :omniauth_providers => [:facebook]
(注意)既にrailas serverを起動していたのなら再起動してomniauthの設定を反映させることが必要です。
現在はDeviseでomniauth model を作ることを許可しただけです。Usertという名前のomniauthのでいるmodelが作られて、config/routes.rb,にdevise_for :usersが既に追加されているならDeviseは以下の様なurlメソッドを作ります。
- user_omniauth_authorize_path(provider)
- user_omniauth_callback_path(provider)
(注意)Deviseは *_url methodsを作る訳ではありません。決してディレクトリの上でcallback helperを使わないでください。facebook認証のためのlayoutの最初の一行に以下を記述するだけで大丈夫です
<%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %>
useromniauth_authorize_pathメソッドに変化する記号はDeviseの構成ブロックにmatchします。passをどうするか確認するためにも"omniauth-* " gem のREADMEをしっかり読んでください。
上のリンクをクリックするとuserはfacebookにredirecteされます。(もしこのリンクが存在しなければサーバーを再起動させようとします)証明書を得た後、あなたのapplicationのcallbackメソッドにridirectされます。callbackを実行するために一番最初にすることはconfig/routes.rb file に戻ってどのcontrollerでcallbackをじっこうするか決まることです。
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
私たちは "app/controllers/users/omniauth_callbacks_controller.rb":を新しく作ったとします。
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
このcallbackはproviderの名前と同じactionとして実行されます。ここではfacebook providerのためにcontrollerに追記します
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
@user = User.find_for_facebook_oauth(request.env["omniauth.auth"])
if @user.persisted?
sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
このactionはいくつかの特徴的な側面があります
1,omniauthによってfacebookから取得する情報すべてがrequest.env["omniauth.auth"]のようなhashで手に入る。どの情報が帰ってくるのか確認のためにOmniauthとomniauth-* gemを確認してください。
2.有効なuserがmodelから送られた場合sign_inするべきです。flash messageにはdefaultのメッセージがsetされていることを覚えておいてください。次に、userがsign inしてredirectします。 :event => :authentication to the sign_in_and_redirect methodを使ったら強制的にすべてのauthenticationのcallbackメソッドがよばれます。
3.userが存在しない場合、Omniauth dataをsessionに保存します。私たちは"deviseというキー名でそれを使うことが出来ます。userがsign inしたときいつでも"devise"から始まるデータをすべて削除することが効果的です。ですから私たちは自動的にsessionをclean upします。最終的に登録フォームにredirectされます。
controllerが定義された後、find_for_facebook_oauth メソッドをmodel(app/models/user.rb)のなかで実行する必要があります
def self.find_for_facebook_oauth(auth)
where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.name = auth.info.name # assuming the user model has a name
user.image = auth.info.image # assuming the user model has an image
user.save!
end
end
(注意) "User.new_with_session" によってDeviseの登録controllerが呼ばれる前にresourceが作られています。セッションからcopyする必要があるならsign_upの前にmodelのなかでnew_with_session を実行してuserがいつでも初期化されているということを意味します。これはfacebook emailをcopyする例です。
class User < ActiveRecord::Base
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
最終的にFacebookでsign upすることをcancelすることを許可したいのなら "cancel_user_registration_path"にredirectさせることも出来ます。これはdeviseで始まるすべてのsessionデータを削除し、new_with_sessionは呼ばれない。
###logout link
devise_scope :user do
get 'sign_out', :to => 'devise/sessions#destroy', :as => :destroy_user_session
end
これで必要なことはすべて終わりました。時間があればtestなんかも書いてみてください。
###omniauthを他のauthoticationなしで使う
もしあなたがomniauthだけをやりたいのならrouteに new_user_session (if not defined, root will be used)を定義する必要があります。下のものはその例です。(omniauth で他のauthoticationやdbを使うならこれは必要ありません)
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
devise_scope :user do
get 'sign_in', :to => 'devise/sessions#new', :as => :new_user_session
get 'sign_out', :to => 'devise/sessions#destroy', :as => :destroy_user_session
end
上の例ではsession controllerでは特別に何かをする必要はありません。