devise recoverableに入力したメールアドレスが間違っていても同じ画面を表示したい

今回はDeviseのお話。

以下のようなアプリケーションを想定。

  1. Deviseを用いて認証
  2. sign up式のユーザ登録ではなく、管理者のコントロール下でユーザ登録される
  3. recoverableを用いてパスワードリセットが可能なようにしたい
  4. 「どのメールアドレスがユーザ登録されているか」という情報は非ユーザ向けに漏出させたくない

問題は4。

Deviseデフォルトのrecoverableが提供するパスワードリセットは、以下のような仕様になっている。

  1. ユーザはメールアドレスを入力してパスワードリセットをリクエスト
  2. メールアドレスが正しければ、画面にはメールを確認するように表示し、パスワード設定ページのワンタイムなURLをメールで送信
  3. ユーザはそのURLにアクセスすると、新たにパスワードを設定できる

今回気にしているのは、1で入力したメールアドレスが正しかった場合と、間違っていた場合(登録されていないメールアドレスだった場合)で、その後の画面表示が異なることである。

https://github.com/plataformatec/devise/blob/v4.2.1/app/controllers/devise/passwords_controller.rb#L16-L20

このように成否によって別画面を表示しているので、第三者が任意のメールアドレスを入力してパスワードリセットのリクエストを行うと、その第三者は、「そのメールアドレスがサービスに登録されているか」を知ることができる。これは最初に示した「4」の要求を満たせない。

多少強引だが、メールアドレスが正しいかどうかに関わらず成功の画面に遷移するように、該当メソッドをオーバーライドすることができる。

元の実装

  def create
    self.resource = resource_class.send_reset_password_instructions(resource_params)
    yield resource if block_given?

    if successfully_sent?(resource)
      respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
    else
      respond_with(resource)
    end
  end

オーバーライド後

  def create
    self.resource = resource_class.send_reset_password_instructions(resource_params)
    yield resource if block_given?

    set_flash_message! :notice, :send_instructions
    respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
  end

もっと良いやり方があれば知りたい。