ruby-on-railsrubybcryptbcrypt-ruby

BCrypt::Errors::InvalidHash in SessionsController#create


I've tried looking at previous questions and answers regarding this error, but the solutions didn't seem to work for me. The error is thrown on the line:

if customer && customer.authenticate(params[:session][:password_digest])

I am able to Sign Up a user (under a company) and sign up a company, but I get this BCrypt error whenever I try to log a user in. I only started using the has_secure_password stuff etc once my user model and company model etc. were in place, I just realised later that it was silly to not protect passwords. Even if I create a new user now, I still get this error logging on.

I think this might be due to my relationships of my Users? (maybe completely not, I am very new to Ruby!) My users belong to a company (many to one), and when they sign up they choose a company from a list in order to be listed under it in the database. Here is my code relating to this error:

class SessionsController < ApplicationController

  def new

  end

  def create
    customer = Customer.find_by_email(params[:session][:email])
    if customer && customer.authenticate(params[:session][:password_digest])
      log_in customer #see sessions helper
      remember customer #see sessions helper
      redirect_to '/main'
    else
      redirect_to '/login'
    end
  end

  def destroy
    #session[:user_id] = nil
    forget(current_customer)
    session.delete(:customer_id)
    @current_customer = nil
    redirect_to '/'
  end
end



class CreateCustomers < ActiveRecord::Migration
  def change
    create_table :customers do |t|
      t.timestamps
      t.references :business, foreign_key: true

      t.timestamps
      t.string :first_name
      t.string :last_name

      t.string :email
      t.string :password_digest
      t.string :remember_digest
      t.string :role
    end
  end
end


class CustomersController < ApplicationController
  def new
    @customer = Customer.new
    @businesses = Business.all
  end

  def create
    @customer = Customer.create(customer_params)
    @customer.save!
    session[:customer_id] = @customer.id
    redirect_to '/'
  rescue ActiveRecord::RecordInvalid => ex
    render action: 'new', alert: ex.message
  end

  private
  def customer_params
    params.require(:customer).permit(:first_name, :last_name, :business_no, :email, :password_digest, :business_id) #replace company with company ID
  end
end

Please help me, I'm tearing my hair out :(


Solution

  • The error you're getting probably means that the password_digest stored for that user is invalid for some reason (empty perhaps). Enter a rails console: rails console and execute the following:

    u = User.find_by(email: "your_user_email")
    puts u.password_digest
    

    and see what the output is.

    (also as mentioned in a comment on your question, you should use the plain text password when you use the authenticate method)

    Update

    You shouldn't use the password_digest attribute directly, instead there are two attributes you should be using: password and password_confirmation (these attributes become available to you automatically when you use has_secure_password, so you don't need to define them).

    So in your controller, instead of this:

    params.require(:customer).permit(:first_name, :last_name, :business_no, :email, :password_digest, :business_id) #replace company with company ID
    

    You should have:

    params.require(:customer).permit(:first_name, :last_name, :business_no, :email, :password, :password_confirmation :business_id) #replace company with company ID
    

    and edit your form accordingly to provide inputs for password and password_confirmation.

    Now when you create your object using these params, it will automatically assign the password digest into password_digest by encrypting the clear text contained in password and password_confirmation.