ruby on rails - How to customize bcrypt? -
i have built working api using rails using this pluralsight blog. in app user verification i've used gems bcrypt , jwt. using mysql database , have user table named users.
which looks this:

saw production database looks this:
since have done quite research , found out while using latest version of bcrypt must have password_digest column, cant store encrypted password , password salt separately.
problem can't change schema of existing table. kindly let me know if there's work around? or there exists better approach tackle issue.
it is possible , isn't difficult. i'll show step-by-step procedure helps understand how that.
how passwords work under hood
we start locating has_secure_password. it's defined in activemodel::securepassword::classmethods. activemodel::securepassword active model concern responsible passwords. it's included activerecord::base.
if at source code can see includes instancemethodsonactivation. defines 4 instance methods:
authenticate- buildbcrypt::passwordbased on value returnedpassword_digest(normally column accessor) , tests whether matchesunencrypted_password.password- ordinary attribute reader.password=- custom attribute setter. converts assignmentpasswordassignmentpassword_digest. remember in ruby these "assignments" method calls. in other words, method translates call#password=call#password_digest=.password_confirmation=- ordinary attribute writer.
based on above code, can see model needs implement 2 methods compatible rails secure passwords. these methods #password_digest , #password_digest=. activemodel::securepassword doesn't care whether backed database columns! perspective, ordinary ruby methods.
implementation
we know #password_digest needs assemble bcrypt digest based on 2 columns in our database. conversely, #password_digest= needs disassemble bcrypt digest salt , checksum.
let's write simple test case ensure code works. our goal make test pass.
require 'test_helper' class usertest < activesupport::testcase name = 'gregnavis'.freeze password = '1234abcd'.freeze test 'passwords work' # first, store user in database check fields # serialized correctly. user.create!(name: name, password: password, password_confirmation: password) # second, reload model ensure works if reset # state isn't serialized database. user = user.find_by_name!(name) # third, let's see whether authentication works. assert(user.authenticate(password)) assert_not(user.authenticate("#{password}1")) end end looking @ source code of #password= can see method receives instance of bcrypt::password argument. the docs bcrypt::password list salt (which includes salt, version, , cost) , checksum attributes. means following implementation should pass test:
class user < activerecord::base has_secure_password def password_digest # need glue 2 parts. "#{password_salt}#{password_hash}" end def password_digest=(digest) # need split bcrypt::password 2 parts. self.password_salt = digest.salt self.password_hash = digest.checksum end end that's it! test green. confirmed joining digest.salt , digest.checksum gives complete hash. did in console.
Comments
Post a Comment