Rails 6 allows configurable attribute on #has_secure_password

Amit Choudhary

By Amit Choudhary

on 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: 'amit.choudhary@bigbinary.com', 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", "amit.choudhary@bigbinary.com"], ["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: "amit.choudhary@bigbinary.com", 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: "amit.choudhary@bigbinary.com", 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: 'amit.choudhary@bigbinary.com', 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", "amit.choudhary@bigbinary.com"], ["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: "amit.choudhary@bigbinary.com", 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: "amit.choudhary@bigbinary.com", 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: "amit.choudhary@bigbinary.com", 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.

Stay up to date with our blogs. Sign up for our newsletter.

We write about Ruby on Rails, ReactJS, React Native, remote work,open source, engineering & design.