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

1>> class User < ApplicationRecord
2>>   has_secure_password
3>> end
4
5=> [ActiveModel::Validations::ConfirmationValidator]
6
7>> user = User.create(email: '[email protected]', password: 'amit.choudhary')
8BEGIN
9User 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"]]
10COMMIT
11
12=> #<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">
13
14>> user.authenticate('amit.choudhary')
15
16=> #<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">
17
18>> class User < ApplicationRecord
19>>   has_secure_password :transaction_password
20>> end
21
22=> NoMethodError: undefined method 'fetch' for :transaction_password:Symbol
23	from (irb):9:in '<class:User>'
24	from (irb):8

Rails 6.0.0.beta2

1>> class User < ApplicationRecord
2>>   has_secure_password
3>>   has_secure_password :transaction_password
4>> end
5
6=> [ActiveModel::Validations::ConfirmationValidator]
7
8>> user = User.create(email: '[email protected]', password: 'amit.choudhary', transaction_password: 'amit.choudhary')
9BEGIN
10User 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"]]
11COMMIT
12
13=> #<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">
14
15>> user.authenticate('amit.choudhary')
16
17=> #<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">
18
19>> user.authenticate_transaction_password('amit.choudhary')
20
21=> #<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.