前置き

Railsでの認証に Devise を使っている人は多いと思います。
ただ、 Devise はデフォルトではフレンドリーフォワーディングが効いていません。

フレンドリーフォワーディングとは、ユーザーが認証前に開こうとしていたページへ、認証後にリダイレクトさせることです。
参考: Rails チュートリアル

最近機会があったので、DeviseのHow Toを参考に実装しました。
その際にやったことを残しておきます。

なお、各バージョンは以下の通りです。

  • Ruby 2.3.1
  • Rails 4.2.7.1
  • Devise 3.5.1

手順

アクセスされたURLを保存する

認証前にアクセスしようとしたページをセッションに保存します。
current_user が取得できれば認証できているので、その場合は除外しています。
また、 devise_controller の各アクションの場合も除外しています。

# app/controllers/application_controller.rb

before_action :store_current_location, unless: :devise_controller?

private

def store_current_location
  return if current_user
  store_location_for(:user, request.url)
end

store_location_for を呼ぶと、session[:user_return_to] にURLを保存してくれます。
:user の部分は認証のscopeです。大抵の場合は :user でOKかと思います。

実は、フレンドリーフォワーディングの実装としてはこれだけで完了です。
session[:user_return_to] にURLが入っていると、認証後にリダイレクトしてくれるようです。

ただ、セキュリティ対策として以下の場合は追加の対応が必要です。

認証後にセッションをリセットする場合

Rails ガイド のセキュリティのページで、セッション固定攻撃の対応策 について紹介されています。
ログイン認証の際に、 reset_session を行なうというものです。
これを行なうと、当然上で保存したURLも消えてしまうので、その値だけ別途引き継ぐ対応を行ないます。

user_return_to を引き継ぐ

Devise::SessionsController を継承したControllerで以下の対応をします。

# app/controllers/sessions_controller.rb

before_action :reset_session_before_login, only: :create

private

def reset_session_before_login
  user_return_to = session[:user_return_to]
  reset_session

  session[:user_return_to] = user_return_to if user_return_to
end

これで、認証時の reset_session をしつつフレンドリーフォワーディングをすることが可能になりました。