yasuoza diary

web and life

devise + omniauth-facebookでリクエストURLにリダイレクトする

deviseomniauth-facebookで簡単にFacebook認証をアプリに組み込むことができます。 組み込み方はdevise公式wikiが一番わかり易いと思いますのでそちらを参考にして下さい。

なお、この記事では次のルーティングに従います。 デフォルトのまま使うと/users/sign_upとかついてきますが、facebookログインに限ると必要ありませんからね。

1
2
3
4
5
devise_for :users, :skip => [:sessions, :registration, :password], :controllers => { :omniauth_callbacks => 'omniauth_callbacks' }
as :user do
  delete '/logout' => 'devise/sessions#destroy', :as => :destroy_user_session
  delete '/delete' => 'devise/registrations#destroy', :as => :destory_user
end

コールバックは次のクラスで受け取ります。

1
2
3
4
5
6
7
8
class OmniauthCallbacksController < Devise::OmniauthCallbacksController

  def facebook
    @user = User.find_or_create_from(request.env['omniauth.auth'])
    sign_in_and_redirect @user
  end

end

それで例えば、/users以下を閲覧するときには認証をかけたいっていうことが往々にしてあると思います。 それは

1
2
3
4
5
6
7
class UsersController < ApplicationController
  before_filter :authenticate_user!

  def show
    @user = current_user
  end
end

のようにしてbefore_filter噛ませてあげると簡単ですが、このままだとユーザが直接/users/:idページを叩いた時にfacebookログインが行われず、基本設定のままだと、ルートにリダイレクトされます。

これを/users/:idページを直接叩いたときは直でfacebookログインにリダイレクトさせて、callbackを受け取った時にもともとリクエストされた/users/:idにリダイレクトさせるには次のように設定します。

1
2
3
4
5
6
7
8
9
class ApplicationController < ActionController::Base
  protect_from_forgery

  protected
  def authenticate_user!
    session[:user_return_to] = env['PATH_INFO']
    redirect_to user_omniauth_authorize_path(:facebook) unless user_signed_in?
  end
end

authenticate_user!をapplication_controller.rbでオーバーライドします。 ポイントは6行目でsession[:user_return_to]にリクエストURLを保存しておくことです。 authenticate_member!の場合はsession[:member_return_to]に保存します。

これでdeviseの内部実装のちからを借りてsign_in_and_redirect_to @userをした時に元のリクエストURLにリダイレクトされます。

devise自体はそんなに難しいことをしていないので、内部実装を覗くとlib/devise/controllers/helpers.rbの次の2つの関数でsession[:user_return_to]からリダイレクト先を取得していることがわかります。

1
2
3
4
5
6
7
8
def stored_location_for(resource_or_scope)
  scope = Devise::Mapping.find_scope!(resource_or_scope)
  session.delete("#{scope}_return_to")
end

def after_sign_in_path_for(resource_or_scope)
  stored_location_for(resource_or_scope) || signed_in_root_path(resource_or_scope)
end

Railsはとても素敵で便利なフレームワークだと思います!Rubyもすごく素敵な言語だなと思います!