ruby-on-railsrubyajaxdevisepassword-recovery

Rails Devise Password Reset Email allowing multiple submissions


I have the following code that allows a user to request a password reset in an AJAX form:

<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post },:remote =>'true') do |f| %>
 <%= devise_error_messages! %>
 <div><%= f.label :email %><br />    
 <%= f.email_field :email %></div>
 <div><%= f.submit "Send me reset password instructions" %></div>
<% end %>

This is allowing the behavior whereby if the user clicks the button repeatedly, or presses "enter" repeatedly, before the server can provide a response, a corresponding # of password reset emails are being sent.

The following is within devise/password_controller.rb

def create
 self.resource = resource_class.send_reset_password_instructions(resource_params)   
 if successfully_sent?(resource)
  flash[:notice] = "You will receive an email with instructions about how to reset your password in a few minutes."
  respond_to do |format|
   format.html #responds with default html file
   format.js 
  end    
 else
  respond_to do |format|
   format.html #responds with default html file
   format.js{ render :js => "$(\".deviseErrors\").html(\"<span class='login-error'>Could not send reset instructions to that address.</span>\");" } #this will be the javascript file we respond with
  end
 end
end

Is there a way to only respond to the first submission?

Thanks


Solution

  • I would recommend to use JavaScript to prevent multiple submissions.

    $('form#reset_password').on('submit', function() {
      $(this).find('input[type="submit"]').attr('disabled', 'disabled')
    })
    

    This will set the submit button as "disabled" status and user can't submit again.

    Reference about form's disabled attribute: http://www.w3schools.com/tags/att_input_disabled.asp*

    Add: Response to thr's answer

    I browsed Devise source and found there should be a solution at model level. To set the max interval allowed between each resetting request, add such in resource model

    class User < ActiveRecord::Base
    
      def self.reset_password_with
        1.day
        # Determine the interval. Any time objects will do, say 1.hour
      end
    end
    

    Then Devise::Models::Recoverable will check this value to decide if a token should be sent. I have not verified this but it should work.