I have an API built with Roda + Sequel stack. Here is how my User
model looks like:
# frozen_string_literal: true
# {User} is model responsible for storing {User} authentication informations like email and password.
#
# @!attribute id
# @return [UUID] ID of the {User} in UUID format.
#
# @!attribute email
# @return [String] Email of the {User}, it's stored in the PostgreSQL citext column.
#
# @!attribute password_hash
# @return [String] {User} hashed password with bcrypt.
#
# @!attribute created_at
# @return [DateTime] Time when {User} was created.
#
# @!attribute updated_at
# @return [DateTime] Time when {User} was updated
class User < Sequel::Model
# It returns instance BCrypt::Password based on value in password_hash column.
#
# @return [BCrypt::Password ] based on value in password_hash column.
#
# @example Get {User} password hash:
# User.new(password: 'test').password #=> "$2a$12$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
def password
@password ||= BCrypt::Password.new(password_hash)
end
# It sets password_hash column with hashed user password.
#
# @return [String] user password hash.
#
# @example Set {User} password:
# User.new(password: 'test').password_hash #=> "$2a$12$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
def password=(new_password)
@password = BCrypt::Password.create(new_password)
self.password_hash = @password
end
end
and I have the following test:
describe 'update user password' do
let(:params) { { password: 'new-password' } }
before do
put '/api/v1/update_password', params
user.reload
end
it 'returns 200 HTTP status' do
expect(response.status).to eq 200
end
This one is failing.
it 'updates user password' do
expect(user.password == 'new-password').to eq true
end
# This one is passing.
it 'updates user password' do
expect(BCrypt::Password.new(user.password_hash).is_password?('new-password')).to eq true
end
end
This example is failing:
it 'updates user password' do
expect(user.password == 'new-password').to eq true
end
but this one is passing:
it 'updates user password' do
expect(BCrypt::Password.new(user.password_hash).is_password?('new-password')).to eq true
end
Could someone explain to me why my first example is failing?
Rewrite this one
it 'updates user password' do
expect(user.password == 'new-password').to eq true
end
to this one
it 'updates user password' do
expect(user.password).to eq(BCrypt::Password.create('new-password'))
end
Your ruby object User
contains hashed version of that password
user.password
=> "$2a$12$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
'$2a$12$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS' == 'new-password'
=> false
and in order to compare them you have to bcrypt your 'new-password' and compare two hashes. You can easily crypt any string with bcrypt, but decrypt it will take much time (years). It is common before verifying password, encrypt your input password
with the same algorithm that your database contains