Skip to content

Instantly share code, notes, and snippets.

@wendygwo
Last active February 21, 2022 14:03
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save wendygwo/6d65db594151c7a9d459 to your computer and use it in GitHub Desktop.
Save wendygwo/6d65db594151c7a9d459 to your computer and use it in GitHub Desktop.
How to add 'Forgot password' to a Rails app that's using bcrypt for authentication

Credit:

All the steps below came from watching the Railscast below:

http://railscasts.com/episodes/274-remember-me-reset-password

The steps below assumes that your app already has a user and user authentication set up.

  • Add to app/views/sessions/new.html.erb file
<p><%= link_to 'Forgot password?', new_password_reset_path %></p>
  • Command line - generate password resets controller and new view:
rails g controller password_resets new
  • Go to routes.rb file:

Delete:

get 'password_resets/new'

Add:

resources :password_resets
  • Replace what's in app/views/password_resets/new.html.erb (forgot password view) with what's below:
<h1>Reset Password</h1>

<%= form_tag password_resets_path, :method => :post do %>
  <div class='field'>
    <%= label_tag :email %>
    <%= text_field_tag :email, params[:email] %>
  </div>
    <div class='actions'>
    <%= submit_tag "Reset Password" %>
  </div>
<% end %>
  • Add create method to app/controllers/password_resets_controller.rb - will reset and send new password. The send_password_reset function is defined two steps after this one
def create
  user = User.find_by_email(params[:email])
  user.send_password_reset if user
  flash[:notice] = 'E-mail sent with password reset instructions.'
  redirect_to new_session_path
end
  • In the command line - Add password_reset_token and password_reset_sent_at columns to user model
rails g migration AddPasswordResetToUsers password_reset_token password_reset_sent_at:datetime
rake db:migrate
  • Add the following code to app/models/user.rb - this contains the send_password_reset function
def send_password_reset
  generate_token(:password_reset_token)
  self.password_reset_sent_at = Time.zone.now
  save!
  UserMailer.forgot_password(self).deliver# This sends an e-mail with a link for the user to reset the password
end
# This generates a random password reset token for the user
def generate_token(column)
  begin
    self[column] = SecureRandom.urlsafe_base64
  end while User.exists?(column => self[column])
end
  • Edit forgot_password method app/mailers/user_mailer.rb- this contains all the different mailer methods Final def password_reset should be:
def forgot_password(user)
  @user = user
  @greeting = "Hi"
  
  mail to: user.email, :subject => 'Reset password instructions'
end
  • Replace what's currently in app/views/user_mailer/forgot_password.html.erb with what's below - contains link to reset password
<%= @greeting %> <%= @user.first_name %>, <br>

To reset your password, click the URL below: <br>

<%= edit_password_reset_url(@user.password_reset_token) %> <!--This is the link to reset your password-->

If you did not request your password to be reset, just ignore this e-mail and your password will stay the same.
  • Add code below to app/config/environments/development.rb - so that the edit_password_reset_url will ink us to the right place
# Configure default URL for action mailer
config.action_mailer.default_url_options = {:host =>'localhost:3000'}
  • Add edit method to app/controllers/password_resets_controller.rb - will let user see the form to edit their password
def edit
  @user = User.find_by_password_reset_token!(params[:id])
end
  • Create app/views/password_resets/edit.html.erb
<h1>Reset Password</h1>

<%= form_for @user, :url => password_reset_path(params[:id]) do |f| %>
  <% if @user.errors.any? %>
    <div class='error_messages'>
      <h2>Form is invalid</h2>
      <ul>
        <% for message in @user.errors.full_messages %>
          <li><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class='field'>
    <%= f.label :password %>
    <%= f.password_field :password %>
  </div>
  <div class='field'>
    <%= f.label :password_confirmation %>
    <%= f.password_field :password_confirmation %>
  </div>
  <div class='actions'><%= f.submit 'Update password' %></div>
<% end %>
  • Add to app/controllers/password_resets_controller.rb - will let user edit their password
def update
  @user = User.find_by_password_reset_token!(params[:id])
  if @user.password_reset_sent_at < 2.hour.ago
    flash[:notice] = 'Password reset has expired'
    redirect_to new_password_reset_path
  elsif @user.update(user_params)
    flash[:notice] = 'Password has been reset!'
    redirect_to new_session_path
  else
    render :edit
  end
end

private
  # Never trust parameters from the scary internet, only allow the white list through.
  def user_params
    params.require(:user).permit(:password)
  end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment