Rails UX Tip: Saving Passwords

Login to Access Code

Login to access video

For some reason, I constantly see developers making a dedicated password update form on a separate page from their profile edit form. When asking the designers and UX experts, most of them tell me that they would rather the password update fields be inline with all the other profile fields, yet time and again developers deviate from this. I asked a few developers why, and the answer: because putting them in the same form would trigger validations and require the user to always update their password when changing other attributes. So today, I aim to correct this problem with a little hack. I do not claim it is the most elegant solution, but it is not a band-aid either and should do for most production applications. Let’s begin.

Only update password if it is actually set

Password validations are important, and we need to keep them in tact. However, if we pass them through from the view, then the validations trigger and we’re left with an error message to the end user that the password cannot be blank. That simply will not do. If we simply create a filter for our create and update actions, we can alleviate this issue:

before_action :check_password_presence, only: [:create, :update]

Now that we have created the filter, as a private method, we simply check if the password fields are blank and if they are, we remove the password and password_confirmation fields from our params hash so that they skip validation:

def check_password_presence
  unless params[:user][:password].present? && params[:user][:password_confirmation].present?
    params[:user].delete(:password)
    params[:user].delete(:password_confirmation)
  end
end

Create a callback on after validation

This may go above and beyond your own applications, but it’s nice to know nonetheless. Say you want to validate an attribute still, when set, and perform action on it. Let’s take a look at how we can do that with a callback, and a custom encrypted field called pin:

class User < ActiveRecord::Base

  validates :pin, length: { minimum: 4 }, numericality: { only_integer: true }, allow_blank: true
  after_validation :ensure_pin_encryption

  private

  def ensure_pin_encryption
    return unless self.pin.present?

    self.pin = Digest::MD5.hexdigest(self.pin)
  end

end

You will definitely want to salt your MD5 at the very least, if not look into the attr_encrypted gem.

That’s our tip of the day. I hope you found it useful!