[Ruby on Rails (RoR)] has_secure_password macro set and authenticate against a BCrypt password

has_secure_password

has_secure_password macro adds methods to set and authenticate against a BCrypt password. This mechanism requires you to have a XXX_digest attribute. Where XXX is the attribute name of your desired password.

has_secure_password(attribute = :password, validations: true)

The following validations are added automatically:

  • Password must be present on creation

  • Password length should be less than or equal to 72 bytes

  • Confirmation of password (using a XXX_confirmation attribute)

If confirmation validation is not needed, simply leave out the value for XXX_confirmation (i.e. don’t provide a form field for it). When this attribute has a nil value, the validation will not be triggered.

For further customizability, it is possible to suppress the default validations by passing validations: false as an argument.

Add bcrypt (~> 3.1.7) to Gemfile to use has_secure_password:

1
gem 'bcrypt', '~> 3.1.7'

Example

Example using Active Record (which automatically includes ActiveModel::SecurePassword - https://api.rubyonrails.org/classes/ActiveModel/SecurePassword.html):

First, create and run migration:

1
2
3
4
5
$ rails new has_secure_password-example

$ rails g model User name:string password_digest:string recovery_password_digest:string

$ rails db:migrate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# app/models/user.rb

# Schema: User(name:string, password_digest:string, recovery_password_digest:string)
class User < ActiveRecord::Base
has_secure_password
has_secure_password :recovery_password, validations: false
end

user = User.new(name: 'david', password: '', password_confirmation: 'nomatch')
user.save # => false, password required
user.password = 'mUc3m00RsqyRe'
user.save # => false, confirmation doesn't match
user.password_confirmation = 'mUc3m00RsqyRe'
user.save # => true
user.recovery_password = "42password"
user.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uXkTG65HR.qpW.bNhEjFP3ftli3o5DQC"
user.save # => true
user.authenticate('notright') # => false
user.authenticate('mUc3m00RsqyRe') # => user
user.authenticate_recovery_password('42password') # => user
User.find_by(name: 'david')&.authenticate('notright') # => false
User.find_by(name: 'david')&.authenticate('mUc3m00RsqyRe') # => user

References

[1] ActiveModel::SecurePassword::ClassMethods - https://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html

[2] Rails 6 allows configurable attribute on #has_secure_password | BigBinary Blog - https://www.bigbinary.com/blog/rails-6-allows-configurable-attribute-name-on-has_secure_password

[3] Cheat Sheet: Simple Authentication in Rails 5 with has_secure_password - https://gist.github.com/iscott/4618dc0c85acb3daa5c26641d8be8d0d

[4] Authentication and Authorization with bcrypt In Rails | Hacker Noon - https://hackernoon.com/authentication-and-authorization-with-bcrypt-in-rails-mw1g3u3l

[4] bcrypt-ruby/bcrypt-ruby: bcrypt-ruby is a Ruby binding for the OpenBSD bcrypt() password hashing algorithm, allowing you to easily store a secure hash of your users’ passwords. - https://github.com/bcrypt-ruby/bcrypt-ruby

[5] bcrypt | RubyGems.org | your community gem host - https://rubygems.org/gems/bcrypt/