Reputation: 812
I observed that the password digest is not working correctly in my rails 4 app - the password digest is being stored in cleartext in the database.
user.rb
class User < ActiveRecord::Base
has_secure_password
validates :password_digest, length: { minimum: 6 }
end
My user db migrate file is as follows:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :full_name
t.string :email
t.string :password_digest
t.timestamps null: false
end
end
end
Any ideas what I am doing wrong?
Edit I have bcrypt enabled in Gemfile (I ran bundle install)
gem 'bcrypt', '~> 3.1.7'
When I try testing in rails console, I get an invalid hash error:
2.2.1 :011 > user = User.new(full_name: 'abcd', email: '[email protected]', password_digest: 'abcdef')
=> #<User id: nil, full_name: "abcd", email: "[email protected]", password_digest: "abcdef", created_at: nil, updated_at: nil>
2.2.1 :012 > User.find_by(full_name: 'david').try(:authenticate, 'abcdef')
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."full_name" = ? LIMIT 1 [["full_name", "david"]]
BCrypt::Errors::InvalidHash: invalid hash
from /usr/local/rvm/gems/ruby-2.2.1/gems/bcrypt-3.1.10/lib/bcrypt/password.rb:60:in `initialize'
from /usr/local/rvm/gems/ruby-2.2.1/gems/activemodel-4.2.4/lib/active_model/secure_password.rb:102:in `new'
from /usr/local/rvm/gems/ruby-2.2.1/gems/activemodel-4.2.4/lib/active_model/secure_password.rb:102:in `authenticate'
Upvotes: 1
Views: 3218
Reputation: 7482
This is strange, everything should work. Try some examples from the docs in your rails console:
user = User.new(name: 'david', password: '', password_confirmation: 'nomatch')
user.save # => false, password required
user.password = 'mUc3m00RsqyRe'
user.save # => false, confirmation doesn't match
user.password_confirmation = 'mUc3m00RsqyRe'
user.save # => true
user.authenticate('notright') # => false
user.authenticate('mUc3m00RsqyRe') # => user
User.find_by(name: 'david').try(:authenticate, 'notright') # => false
User.find_by(name: 'david').try(:authenticate, 'mUc3m00RsqyRe') # => user
If user.save
returns false
you may look into user.errors
for failed validation messages.
EDIT: @madcow finally sorted it right. Right now you need to convert your plain-text passwords, stored in password_digest
to bcrypt hashes.
NOTE: You won't be able to restore plain-text passwords back using this code! Make sure to backup your table before-hand if you need plain-text passwords.
Try this code for a single user, and, if everything is ok, apply this technique to other users:
user = User.find_by(name: 'david')
user.password = user.password_confirmation = user.password_digest
user.save!
Here we assign to user.password
your plain-text password from user.password_digest
. Since has_secure_password
redefines password=
method, your plain-text password should effectively get stored back to password_digest
column, but now as a hash.
To update all users:
User.find_each do |user|
user.password = user.password_confirmation = user.password_digest
user.save!
end
Upvotes: 3
Reputation: 2633
You are accidently saving the cleartext password directly in the password digest field, at least in your console example. That is why the password is stored as cleartext and not hashed.
Instead of typing this in your console:
user = User.new(full_name: 'abcd', email: '[email protected]', password_digest: 'abcdef')
Enter this:
user = User.new(full_name: 'abcd', email: '[email protected]', password: 'abcdef', password_confirmation: 'abcdef')
user.save
Then find the user:
User.find_by(full_name: 'abcd').try(:authenticate, 'abcdef')
Never set the :password_digest
directly. Always set it through the :password
and :password_confirmation
fields.
You are getting that invalid hash error, by the way, because it is trying to decrypt your cleartext (not a valid hash) :password_digest
value.
Upvotes: 2