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:

current user table
saw production database looks this: desired user table

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:

  1. authenticate - build bcrypt::password based on value returned password_digest (normally column accessor) , tests whether matches unencrypted_password.
  2. password - ordinary attribute reader.
  3. password= - custom attribute setter. converts assignment password assignment password_digest. remember in ruby these "assignments" method calls. in other words, method translates call #password= call #password_digest=.
  4. 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

Popular posts from this blog

Is there a better way to structure post methods in Class Based Views -

performance - Why is XCHG reg, reg a 3 micro-op instruction on modern Intel architectures? -

jquery - Responsive Navbar with Sub Navbar -