Skip to content

Instantly share code, notes, and snippets.

@MilanGrubnic70
Forked from paigecrum/bcrypt_notes.md
Last active August 29, 2015 14:01
Show Gist options
  • Save MilanGrubnic70/26f15aa76954f585756b to your computer and use it in GitHub Desktop.
Save MilanGrubnic70/26f15aa76954f585756b to your computer and use it in GitHub Desktop.

##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 of bcrypt in the aforementioned files but bcrypt-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 called password_digest, instead, you need one called password_hash.

Rails Guide states:

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.)

# require 'bcrypt' required it in enviroment

class User < ActiveRecord::Base
  # users.password_hash in the database is a :string

  # include BCrypt # module

  # a fieldin your users table called password_hash
  # getter method
  def password
    @password ||= BCrypt::Password.new(password_hash)
  end

  #setter method
  def password=(new_password)
    @password =  BCrypt::Password.create(new_password)
    self.password_hash = @password
  end

  def self.authenticate(email, password)
  	# return a user if the email and password are good
  	# return nil otherwise
  	# user = self.find_by(email: email)
  	user = self.find_by_email(email)
  	
  	if user && user.passord == password
  		user
  	else
  		nil
  	end
  end
end
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])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment