[Rails]Omniauthで戻り先URLを指定する


[まとめ] 現在開催中のKindleセール情報はこちら

Ruby2.0, Rails4.1で確認

先日、RailsでOmniauthを使ってTwitterログインする
方法をまとめました。

その後、色々改良していたところ、ログイン後に必ずroot_pathにリダイレクトさせるのではなく、URLパラメータに戻り先URLを入れて動的にログイン後の遷移を変更したくなりました。

そこで調べてみたところ、こちらの記事にやり方が紹介されていました。

Omniauthを使っている場合は、URLにoriginパラメータを指定すればうまいことやってくれるそうです。

お手軽ですね。

前回のSessionContrllerを改良します。

  • 前回のSessionController
# app/controllers/sessions_controller.rb

class SessionsController < ApplicationController
  def callback
    auth = request.env['omniauth.auth']
    user = User.find_by_provider_and_uid(auth['provider'], auth['uid']) || User.create_with_omniauth(auth)
    session[:user_id] = user.id
    redirect_to root_path
  end
   
  def destroy
    session[:user_id] = nil
    redirect_to root_path
  end
end
  • これをこう
# app/controllers/sessions_controller.rb

class SessionsController < BaseController
  def callback
    auth = request.env['omniauth.auth']
    # 複数のサービスに対応させるために前回から少し変更
    session[auth['provider'].to_sym] = {
      :user_id => auth['uid'],
      :name => auth['info']['name'],
      :oauth_token => auth['credentials']['token'],
      :oauth_token_secret => auth['credentials']['secret']
    }

    # originが指定されており、同一ドメインの場合は指定の戻り先にリダイレクト
    redirect_to back_to || root_path
  end

  def destroy
    reset_session
    redirect_to root_path
  end

  private
  def back_to
    if request.env['omniauth.origin'].presence && back_to = CGI.unescape(request.env['omniauth.origin'].to_s)
      uri = URI.parse(back_to)
      return back_to if uri.relative? || uri.host == request.host
    end
    nil
  rescue
    nil
  end
end

これで、ログイン時(/auth/twitterや/auth/facebookアクセス時)に?origin=/hogeが指定されていたら、ログイン後に/hogeにリダイレクトされるようになります。

before_actionで認証が必要なページにアクセスした場合もoriginを渡せるようにしておきましょう。

今回はfacebookの場合のサンプルを載せておきます。

loginとwallというURLを追加してみます。

loginはfacebook認証不要、wallはfacebook認証が必要なページです。

  • routing
# config/routes.rb

get "login", :to => "sample#login", :as => :login
get "wall", :to => "sample#wall", :as => :wall
  • SampleController
# app/controllers/sample_controller.rb

class SampleController < BaseController
  # facebook認証チェック
  before_action :facebook_login_required, only: [:wall]

  # loginはfacebook認証が不要
  def login
  end

  # wallはfacebook認証が必要
  def wall
  end
end
  • loginテンプレートにwallへのリンクを表示
# app/views/sample/login.html.erb

<%= link_to 'facebook_login', wall_path(:origin => '/wall') %>
  • BaseControllerにfacebook認証チェックを追加
# app/controllers/base_controller.rb

class BaseController < ApplicationController

  # facebook未ログイン時に facebook_oauth_path へリダイレクトする
  def facebook_login_required
    if !facebook_logged_in?
      # originが含まれている場合はパラメータに追加
      if params['origin'].blank?
        redirect_to '/auth/facebook'
      else
        redirect_to '/auth/facebook?origin=' + params['origin']
      end
    end
  end

  private
  def facebook_logged_in?
    (session[:facebook] && session[:facebook][:user_id]) ? true : false
  end
end

facebook_login_requiredの部分で、受け取ったoriginパラメータを渡すようにすることでbefore_action経由でもうまく戻り先URLに戻れるようになります。

参考

[まとめ] 現在開催中のKindleセール情報はこちら