No commit activity in last 3 years
No release in over 3 years
HasEditablePassword extends has_secure_password with updating capabilities. On password update, the old password or a recovery token is asked.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

has_editable_password

HasEditablePassword extends has_secure_password with updating capabilities. On password update, the old password or a recovery token is asked.

Installation

Add to gemfile:

gem 'has_secure_password'

Install:

bundle install

With ActiveRecord:

Generate a migration:

rails g migration add_password_fields_to_user password_digest:string password_recovery_token:string password_recovery_token_created:datetime

Migrate:

rake db:migrate

Include:

class User < ActiveRecord::Base
  include HasEditablePassword
end

With Mongoid:

Include and add fields:

class User
  include Mongoid::Document
  include HasEditablePassword
  
  field :password_digest, type: String
  field :password_recovery_token, type: String
  field :password_recovery_token_creation, type: DateTime
end

Usage

Now you have an has_secure_password equivalent. It works just like that.

To update the password, given the old one:

class UserController
  def update
    @user = User.find(params[:id])
    if @user.update_attributes(params.require(:user).permit(:current_password, :password, :password_confirmation))
      render :show
    else
      # password didn't match OR current_password didn't match
      render :edit
    end
  end
end

When submitting a password recovery form:

class PasswordRecoveryController
  def create
    @user = User.find_by_email(params[:email])
      
    token = @user.generate_recovery_token
    url = edit_password_recovery_url(@user, token: token)
      
    # send url by email
    # render "An email has been sent with a link
  end
    
  # Accessed by email link
  def edit
    @user = User.find(params[:id])

    if @user.valid_recovery_token?(params[:token])
      render :show
    else
      flash[:error] = "Invalid token"
      redirect_to root_path
    end
  end
   
  def update
    @user = User.find(params[:id])
    @user.recovery_token = params[:token]
    
    if @user.update_attributes(params.require(:user).permit(:password, :password_confirmation)
      # login
      flash[:notice] = "Success"
      redirect_to root_path
    else
      # Invalid token or password did not match
      redirect_to root_path
    end
  end
end

Old password backup

If you want to keep a backup of the previous password (only the last one), just add another attribute:

rails g migration add previous_password_digest:string

or

field :previous_password_digest, type: String

You can use this for reminders or security alerts (the user using an old password is probably a malicious user with a stolen password).

Last password change timestamp

If you need password change timestamp add the following attribute:

rails g migration add password_digest_updated:date

or

field :password_digest_updated, type: DateTime

This could be used for password expiration mechanisms or in combination with the backup to tell the user (or attacker):

Man, you don't use that password anymore! You changed it a month ago!