- 更新日: 2017年2月15日
- Devise
Rails + Devise 環境でのフレンドリーフォワーディング機能を修正
Rails の Devise でログイン後にログインする直前のページにリダイレクトさせる | EasyRamble で書いていた、Devise 利用時のフレンドリーフォワーディング機能の実装コードにバグがありましたので修正しました。
— 環境 —
Rails 5.0.0.1
Devise 4.2
【追記 2016/11/08】
Rails 5 + Devise 4.2 の最新バージョンで実装して動作確認を行いましたところ、正常に動作することを確認いたしました。
【追記ここまで】
— 記事初回公開時の環境 —
Rails 4.0.1
Devise 3.2.2
Devise でのパスワードリセット機能
Devise で Recoverable モジュールを有効にすると、パスワードリセット機能が使えます。Devise の Recoverable モジュールでパスワードリセットする場合、ウェブページの操作は以下の流れとなる。
1. ユーザーがログイン画面でパスワードを忘れましたか?をクリックして /users/password/new にアクセス。
2. フォームにメアドを入力して、パスワードリセット要求のメールを送信する。
3. メール中のリセットパスワードトークンを含むURLをクリックして、/users/password/edit?reset_password_token=***** にアクセス。
4. フォームに新しいパスワードを入力して送信して、新しいパスワードに更新する。
以上の流れになりますが、この 4 の後のリダイレクト部分でエラー発生。ブラウザで「ページの自動転送設定が正しくありません。このアドレスへのリクエストに対するサーバの自動転送設定がループしています。」というリダイレクトエラー時に見るエラーが発生しました。
ApplicationController#store_location を修正
Devise::PasswordsController と Recoverable モジュールのソースを読んで修正したのですが、結論を言うと ApplicationController に定義していた store_location メソッドを以下のように修正しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class ApplicationController < ActionController::Base # ... after_action :store_location def store_location if (request.fullpath != new_user_registration_path && request.fullpath != new_user_session_path && # request.fullpath != "/users/password" && request.fullpath !~ Regexp.new("\\A/users/password.*\\z") && !request.xhr?) session[:previous_url] = request.fullpath end end def after_sign_in_path_for(resource) if (session[:previous_url] == root_path) super else session[:previous_url] || root_path end end # ... end |
パスワードリセット機能を用いる時は、session[:previous_url] を保存しないようにと考えて、
1 |
request.fullpath != "/users/password" && |
と if 文に書いていたのですけど、これだと全然駄目ですね。/users/password/new や /users/password/edit?reset_password_token=***** のURLが session[:previous_url] に入ってしまいます。これがバグの原因でした。なので、この部分を以下のように修正。
1 |
request.fullpath !~ Regexp.new("\\A/users/password.*\\z") && |
/users/password/new や /users/password/edit?reset_password_token=***** など /users/password でパスが始まるURLは session[:previous_url] に保存しないようにします。
【追記 2017/02/15】
さらにもう一歩進めて、store_location メソッドの可読性を改善しました。
1 2 3 4 5 6 7 8 9 |
def store_location if request.fullpath.in?( [new_user_registration_path, new_user_session_path] ) || request.fullpath =~ Regexp.new("\\A/users/password.*\\z") || request.xhr? return end session[:previous_url] = request.fullpath end |
【追記ここまで】
Devise::PasswordsController と Devise Recoverable Module のソース読み
Devise のソースを読んで理解した部分を、簡単にまとめておきます。
new
/users/password/new
パスワードリセット用のメールアドレスを入力させるフォームを表示させるページ。
create
パスワードリセット要求メールを送信させるアクション。メール送信フォームをサブミットして成功した後に、after_sending_reset_password_instructions_path_for メソッドで指定したページにリダイレクトさせる。
How To: Redirect URL after sending reset password instructions
edit
/users/password/edit?reset_password_token=*****
メール中のリセットパスワードトークンを含むURLをクリックした後に、新しいパスワードの入力フォームを表示させる。
update
新しいパスワードの入力フォームがサブミットされた時に、パスワードを更新するアクション。成功したら after_resetting_password_path_for で指定してページにリダイレクト。この after_resetting_password_path_for の中身は、after_sign_in_path_for を呼んでいるだけ。
ということで、フレンドリーフォワーディング機能を作った際にオーバーライドした after_sign_in_path_for でミスったな…と見当がつきました。
send_reset_password_instructions, reset_password_by_token など Devise::PasswordsController で使われるメソッドが定義されている。
以上ですが、Devise に関してはかなり慣れてきたので、何か問題が起こったらソースを読めば大体解決できる感触があります。慣れた gem に関しては、下手にググるより公式ドキュメントとソース読んだほうが速い感じですね。
最後ちなみに gem のソース読む時は、bundler で入れているのであれば…
1 2 3 |
$ bundle open devise |
ってやると楽です。
- Devise の関連記事
- RailsのDevise認証機能での実装チェックリストまとめ
- Deviseで送信されるメールのfrom(送信者メールアドレス)を変更
- Facebook の OAuth 認証で OAuthException(191)エラー
- Rails Devise でパスワードリセットなどのメールテンプレート(Mailer ビュー)をカスタマイズ
- Deviseでユーザー登録完了時にウェルカムメールを送信する
- Rails Devise でユーザーがプロフィール情報を更新後に元のページにリダイレクトさせる
- Devise でユーザーがパスワードなしでアカウント情報を変更するのを許可
- Rails Deviseの日本語化辞書ファイル(devise.ja.yml)
- Rails + Devise で admin ユーザー(管理者)を削除できないようにする
- Devise3.2.2 のデフォルト設定では、Rememberable の remember_token のカラムがないのでソースを解読してみた
- 初回公開日: 2014年6月9日
Leave Your Message!