50

I am trying to finish a project. I am working with user models. When I signup everything seems ok . But when I try to signin the same member I get this error.

We're sorry, but something went wrong. heroku logs file shows error as:

BCrypt::Errors::InvalidHash (invalid hash):
  app/controllers/sessions_controller.rb:8:in `create'

my *sessions_controller* is :

class SessionsController < ApplicationController

  def new
  end

   def create
    user = User.find_by_email(params[:session][:email])
    if user && user.authenticate(params[:session][:password])
      sign_in user
      redirect_to user
    else
      flash.now[:error] = 'Invalid email/password combination'
      render 'new'
    end
  end


  def destroy
    sign_out
    redirect_to root_path
  end
end

and user model is :

class User < ActiveRecord::Base
  attr_accessible :email, :name, :nickname,:password, :password_confirmation 
  has_secure_password


  before_save { |user| user.email = email.downcase }
  before_save { |user| user.nickname = nickname.downcase }
  before_save :create_remember_token
....validations......

    private

    def create_remember_token
      self.remember_token = SecureRandom.urlsafe_base64
    end
end 

this is my session.helper

module SessionsHelper

  def sign_in(user)
    cookies.permanent[:remember_token] = user.remember_token
    self.current_user = user
  end
  def signed_in?
    !current_user.nil?
  end

  def current_user=(user)
    @current_user = user
  end

  def current_user
    @current_user ||= User.find_by_remember_token(cookies[:remember_token])
  end

  def sign_out
    self.current_user = nil
    cookies.delete(:remember_token)
  end
end

I tried heroku rake db:migrate, heroku restart.. there is no change.

Ben Lee
  • 50,019
  • 12
  • 118
  • 142
ytsejam
  • 2,893
  • 7
  • 32
  • 61
  • 1
    can you show us your `sign_in` method? – Jason Kim Jun 14 '12 at 17:11
  • Edited my first message with session.helper in the message.there is the sign_in method – ytsejam Jun 14 '12 at 17:18
  • Just in case, have you done `bundle install` with `gem 'bcrypt-ruby', '3.0.1'` in your `Gemfile`? – Jason Kim Jun 14 '12 at 17:20
  • yes bcrypty-ruby and and for production " pg "gem. – ytsejam Jun 14 '12 at 17:25
  • @ytsejam, Did you recently switch to using "has_secure_password" and were using a different authentication scheme before, and have an existing user database? – Ben Lee Jun 14 '12 at 17:26
  • Can you try this? In sessions_helper.rb, for `sign_in` method, put `current_user = user` instead of `self.current_user = user`. BTW, does this work in Dev environment? – Jason Kim Jun 14 '12 at 17:29
  • Ben lee , yes i changed the has_secure_password. before i used the wrong code with attr-accessor :password , :password_combination – ytsejam Jun 14 '12 at 17:31
  • @ytsejam, I don't really understand. What I'm asking is did you switch from using SHA1 or something like that, to BCrypt? – Ben Lee Jun 14 '12 at 17:32
  • no i did not change. but i made a mistake in this line attr_accessible :email, :name, :nickname,:password, :password_confirmation has_secure_password ( before i was trying attr_accessor) in this line – ytsejam Jun 14 '12 at 17:33
  • garbage collection .. it tells me invalid hash and the error is in this line if user && user.authenticate(params[:session][:password]) – ytsejam Jun 14 '12 at 17:37
  • @JasonKim I am seeing the same error and have `bundle install` with `gem 'bcrypt-ruby', '3.0.1'`. Any suggestions? – chetang Dec 18 '20 at 16:12

3 Answers3

89

This means that the hash stored in password_digest is not a valid BCrypt hash (including if the field is empty).

Based on the comments, it looks like you just created the user at a time the has_secure_password wasn't there, so the password digest never got stored. Look in the database, you'll probably see that password_digest is empty for that user. Remove the user from the database and re-create with your new working code and it should work.

While discussing with in the comments though, I made an (incorrect) guess about why the passwords would be wrong, and I already wrote up the explanation. So here it is for any future visitor that does have this problem, even though it doesn't apply directly here:


This typically happens when you switch from using SHA1 or another algorithm to BCrypt but fail to re-hash the passwords in BCrypt. Since you don't have access to the original passwords (or at least you shouldn't...), it's a bit ugly to switch because you have to use both BCrypt and the original authentication scheme. For example, if you were using SHA1 before and now use BCrypt, you have to treat the SHA1 password hash as the plain text password for BCrypt input. For example, you might create a BCrypt digest like this:

sha1_password = Digest::SHA1.hexdigest("#{salt}#{real_password}")
self.password_digest = BCrypt::Password.create(sha1_password).to_s

Then, you can create bcrypt password_digests based on the sha1 password hashes that you do have access to.

You would authenticate like this:

sha1_password = Digest::SHA1.hexdigest("#{salt}#{attempted_password}")
BCrypt::Password.new(self.password_digest) == sha1_password

I used SHA1 in the above examples, but this will work for other hashing algorithms as well.

Ben Lee
  • 50,019
  • 12
  • 118
  • 142
  • Saved me time. Thanks! – Kirk Nov 13 '13 at 05:39
  • I am using Devise and trying to migrate my database from Wordpress 4 ( i think it uses phpass). I get the invalid hash. Where do I write this BCrypt Digest so it can authenticate the passwords hashed from Wordpress? – Luiz Henrique Aug 27 '17 at 21:20
12

I already had live users, and likewise already saved unencrypted passwords into the database. Once I started using bcrypt, it was expecting an encrypted password, and when it didn't find it, it produced this error.

Therefore, I added this rescue to catch the error and prompt the legacy users to reset their password:

begin
    # your code that attempts to login the user
rescue BCrypt::Errors::InvalidHash
  flash[:error] = 'We recently adjusted the way our passwords are stored. Please click the "forgot username or password?" link to re-establish your password. Thank you for your understanding!'
  redirect_to password_resets_url
end 

Hope this helps.

nfriend21
  • 2,022
  • 1
  • 18
  • 21
  • 3
    I don't think it's a good idea to rely on authenticate() raising an undocumented exception; it's cleaner to work around it by checking password_digest.empty? before calling authenticate(). – pdg137 Sep 02 '15 at 00:42
  • I also have existing users (though a small set). I manually updated passwords with `u = User.find(...); u.password = ""; u.save!` and sent everyone the new passwords. The point here i guess is that simply updating the password via the model, sets everything up properly. – mr rogers Sep 22 '16 at 03:31
4

An update to the BCrypt gem fixed this issue for me

Ashley
  • 121
  • 1
  • 3
  • In my case, it seemed to be specific to the system. Since the same thing with the old version worked on another machine. So I updated the version of gem and worked for me. – Pragati Wagh Jan 13 '21 at 06:11
  • This solved my problem too when I cloned https://github.com/gorails-screencasts/two-factor-authentication – Milind Mar 01 '21 at 19:27
  • In my case I went from bcrypt 3.1.11 to 3.1.16 and it resolved the error. https://github.com/heartcombo/devise/issues/4861 seems related. My previous password didn't work, I had to reset it. – josephdpurcell Apr 23 '21 at 01:16