Rails 6 allows configurable attribute on #has_secure_password

Amit Choudhary

Amit Choudhary

April 23, 2019

This blog is part of our  Rails 6 series.

has_secure_password is used to encrypt and authenticate passwords using BCrypt . It assumes the model has a column named password_digest.

Before Rails 6, has_secure_password did not accept any attribute as a parameter. So, if we needed BCrypt encryption on a different column other than password_digest, we would have to manually encrypt the value before storing it.

Rails 6 makes it easy and allows custom attributes as a parameter to has_secure_password. has_secure_password still defaults to password so it works with previous versions of Rails. has_secure_password still needs the column named column_name_digest defined on the model.

has_secure_password also adds the authenticate_column_name method to authenticate the custom column.

Let's check out how it works.

Rails 5.2

>> class User < ApplicationRecord
>>   has_secure_password
>> end

=> [ActiveModel::Validations::ConfirmationValidator]

>> user = User.create(email: '[email protected]', password: 'amit.choudhary')
BEGIN
User Create (0.8ms)  INSERT INTO "users" ("email", "password_digest", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["email", "[email protected]"], ["password_digest", "$2a$10$g6ZJNgakn4I1w/qjAx3vM.I76QSNjFCHtTtT9ovko/9Th50SEmIBO"], ["created_at", "2019-03-17 23:30:13.754379"], ["updated_at", "2019-03-17 23:30:13.754379"]]
COMMIT

=> #<User id: 1, email: "[email protected]", password_digest: "$2a$10$g6ZJNgakn4I1w/qjAx3vM.I76QSNjFCHtTtT9ovko/9...", created_at: "2019-03-17 23:30:13", updated_at: "2019-03-17 23:30:13">

>> user.authenticate('amit.choudhary')

=> #<User id: 1, email: "[email protected]", password_digest: "$2a$10$g6ZJNgakn4I1w/qjAx3vM.I76QSNjFCHtTtT9ovko/9...", created_at: "2019-03-17 23:30:13", updated_at: "2019-03-17 23:30:13">

>> class User < ApplicationRecord
>>   has_secure_password :transaction_password
>> end

=> NoMethodError: undefined method 'fetch' for :transaction_password:Symbol
	from (irb):9:in '<class:User>'
	from (irb):8

Rails 6.0.0.beta2

>> class User < ApplicationRecord
>>   has_secure_password
>>   has_secure_password :transaction_password
>> end

=> [ActiveModel::Validations::ConfirmationValidator]

>> user = User.create(email: '[email protected]', password: 'amit.choudhary', transaction_password: 'amit.choudhary')
BEGIN
User Create (0.5ms)  INSERT INTO "users" ("email", "password_digest", "transaction_password_digest", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["email", "[email protected]"], ["password_digest", "$2a$10$nUiO7E2XrIJx/sSdpG0JAOL00uFvPRH7kXHLk5f/6qA1zLPHIrpPy"], ["transaction_password_digest", "$2a$10$l6cTpHwV9xOEn2.OumI29OnualGpvr1CgrNrbuMuHyGTltko8eBG2"], ["created_at", "2019-03-17 23:42:28.723431"], ["updated_at", "2019-03-17 23:42:28.723431"]]
COMMIT

=> #<User id: 5, email: "[email protected]", password_digest: [FILTERED], transaction_password_digest: [FILTERED], created_at: "2019-03-17 23:42:28", updated_at: "2019-03-17 23:42:28">

>> user.authenticate('amit.choudhary')

=> #<User id: 5, email: "[email protected]", password_digest: [FILTERED], transaction_password_digest: [FILTERED], created_at: "2019-03-17 23:42:28", updated_at: "2019-03-17 23:42:28">

>> user.authenticate_transaction_password('amit.choudhary')

=> #<User id: 5, email: "[email protected]", password_digest: [FILTERED], transaction_password_digest: [FILTERED], created_at: "2019-03-17 23:42:28", updated_at: "2019-03-17 23:42:28">

Here is the relevant pull request.

If this blog was helpful, check out our full blog archive.

Stay up to date with our blogs.

Subscribe to receive email notifications for new blog posts.