author thumb

Software Developer

Devise update user without password

Devise is a extraordinary gem to help you with a authentication flow in your rails application. It comes with the entire registration logic pre-configured to work like a charm, but sometimes we need to customize it to work for specific cases, right?

In this post i’ll show you how to require Password on a user update, but only when the current user try to change specific fields, like a username or e-mail. (By default devise will require password for every change)

To get started we need to override your registration controller. To do that create a Registration controller, and change it to be a Subclass of Devise.

rails g controller Registrations update

Edit your registrations_controller.rb file created to look like this:

class RegistrationsController < Devise::RegistrationsController
	def update
	end
end

Now we’ll override the update function in Registrations Controller.

def update
    # Search user by id
    @user = User.find(current_user.id)
    # Verify if we need to request current password from user
    successfully_updated = if needs_password?(@user, params)
                             @user.update_with_password(params[:user])
                           else
                             # Remove the virtual current_password attribute
                             # update_without_password
                             params[:user].delete(:current_password)
                             @user.update_without_password(params[:user])
                           end
    if successfully_updated
      set_flash_message :notice, :updated
      # Sign in the user bypassing validation in case his password changed
      sign_in @user, :bypass => true
      redirect_to after_update_path_for(@user)
    else
      render "edit"
    end
  end

As you can see, this code calls needs_password? to verify what fields need password to be updated. Check the code bellow:

private
  def needs_password?(user, params)
    # Verify if email changed
    user.email != params[:user][:email] ||
        # Verify if the password has been informed
        params[:user][:password].present? ||
        # Verify if username changed
        user.username != params[:user][:username]
  end

You can see the final code here:

class RegistrationsController < Devise::RegistrationsController
  def update
    # Search user by id
    @user = User.find(current_user.id)
    # Verify if we need to request current password from user
    successfully_updated = if needs_password?(@user, params)
                             @user.update_with_password(params[:user])
                           else
                             # Remove the virtual current_password
                             # attribute update_without_password
                             params[:user].delete(:current_password)
                             @user.update_without_password(params[:user])
                           end
    if successfully_updated
      set_flash_message :notice, :updated
      # Sign in the user bypassing validation in case his password changed
      sign_in @user, :bypass => true
      redirect_to after_update_path_for(@user)
    else
      render "edit"
    end
  end
  private
  def needs_password?(user, params)
    # Verify if email changed
    user.email != params[:user][:email] ||
        # Verify if the password has been informed
        params[:user][:password].present? ||
        # Verify if username changed
        user.username != params[:user][:username]
  end
end

Now if you want another field to be required on change, just add them in needs_password? function.

author thumb

Hugo Dias

Desenvolvedor Back-end & Front-end, Desenvolvimento Android e IOS, HTML5, CSS3 e JavaScript