##BCrypt
http://bcrypt-ruby.rubyforge.org/
BCrypt is a gem. In order to use BCrypt in your Sinatra or Rails applications, you must include gem 'bcrypt'
in your Gemfile and require 'bcrypt'
in your environment.rb
file.
Note: You might have seen
bcrypt-ruby
used in place ofbcrypt
in the aforementioned files butbcrypt-ruby
is supposedly deprecated, though a quick Google search doesn't return much on that.
###has_secure_password
BCrypt allows us to use the Rails concept of has_secure_password
. We put this in our User
model.
class User < ActiveRecord::Base
has_secure_password
end
has_secure_password
saves us time by adding methods that set and authenticate against a BCrypt password.
To use has_secure_password
, you must have a field/column in your users
table called password_digest
(is a :string
).
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.string :password_digest
t.timestamps
end
end
end
Note: When using BCrypt normally (without has_secure_password), you do not have a column on your
users
table calledpassword_digest
, instead, you need one calledpassword_hash
.
Validations for presence of password on create
and confirmation of password (using a password_confirmation
attribute) are automatically added. If you wish to turn off validations, pass validations: false
as an argument. You can add more validations by hand if need be.
If you don't need the confirmation validation, just don't set any value to the password_confirmation
attribute and the validation will not be triggered.
In your HTML user sign-up form that asks a user for their password, you must have an <input>
for both password
and password_confirmation
(these are the values for the name
attributes).
<form action="/register" method="post">
<input type="text" name="email">
<input type="password" name="password">
<input type="password" name="password_confirmation"> <!-- optional? -->
<input type="submit" value="Sign Up!">
</form>
Note: You do not need to include the password_confirmation
<input>
on your HTML registration form when using BCrypt without has_secure_password
. You only need to ask for the password.
###BCrypt without has_secure_password
You can use BCrypt without the has_secure_password
method, and you likely should if you do not understand what has_secure_password
is taking care of for you.
If you don't use has_secure_password
, you still must require 'bcrypt'
in your environment and include the gem (gem 'bcrypt'
) in your Gemfile, but you will instead need to include BCrypt's reader/writer methods in your User
model. You can copy and paste these from http://bcrypt-ruby.rubyforge.org/. You will still need to write your own method to authenticate a user-provided email (or username, etc.) and password. An example is below.
class User < ActiveRecord::Base
# users.password_hash in the database is a :string --rather than password_digest
include BCrypt
def password
@password ||= Password.new(password_hash)
end
def password=(new_password)
@password = Password.create(new_password)
self.password_hash = @password
end
def self.authenticate(email, password)
user = User.find_by_email(email)
return user if user && (user.password == password) # *Note: See below.
nil # return nil if either invalid email or wrong password provided
end
end
*Note: The password method called on user in the above example is BCrypt's password "reader" method that we also just defined within the User
model. This is not referencing a field in our users
table. (Remember, we called the only password-related field in our users
table password_hash
.)
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.string :password_hash
t.timestamps
end
end
end
Also, be aware that when creating a new instance of the User
class somewhere in your program, you will not specify the password attribute as password_hash
, but as password
. (Again, this is utilizing the BCrypt methods we've included.)
Correct:
User.create(email: params[:email], password: params[:password])
Incorrect:
User.create(email: params[:email], password_hash: params[:password])