Lesson 4, Step 4 - Signup: "password", not "password_hash"?


#1

I wasn't able to complete this exercise without using the "Get Code" button and I'm a little confused by the solution it provides:

<%= form_for(@user) do |f| %>
  <%= f.text_field :first_name, :placeholder => "First name" %>
  <%= f.text_field :last_name, :placeholder => "Last name" %>
  <%= f.email_field :email, :placeholder => "Email" %>
  <%= f.password_field :password, :placeholder => "Password" %>
  <%= f.submit "Create an account", class: "btn-submit" %>
<% end %>

Although I don't remember the course ever making reference to :placeholder or different kinds of fields besides text_field, that's not what's confusing me.

The model doesn't have a password field. It has a string field named password_hash used for storing the encrypted version of the password.

I figured I needed to create a text_field or password_field for the password and then encrypting it and storing it in the model would be covered in the next step.

If form_for is tied to a model and its fields, how does f.password_field :password... work since there's no password field?

Is Rails handling encrypting the password and storing it in the database here, or are we just not doing that part yet? If we're not encrypting and storing the password yet, where does that plain-text password go after I type it and submit the form?

Thank you.


#2

First, thanks for posting this cause I've been stuck on the forms as well and I hate using the get code option. Second, to confirm your suspicion that there's never been mention of :placeholder I don't recall ever seeing it mentioned in any of the examples and it's definitely not part of the one they reference.

I think the password field arises from the CSS:
`/* FORM PAGES */

.form {
text-align:center;
margin: 0 auto 10px;
padding: 20px 0 10px 0;
font-size: 16px;
width: 300px;
}

input[type=text],
input[type=email],
input[type=password] {
color: #666666;
width: 100%;
border: 1px solid #E6E6E6;
border-radius: 0px;
height: 60px;
margin: 6px auto;
text-indent: 10px;
}

.btn-submit {
color: #FFFFFF;
font-size: 18px;
text-transform: uppercase;
background: #c91717;
padding: 10px 20px;
border: 0;
margin-top: 6px;
}
`

And I believe it mentioned in the exercise after activating bcrypt, that bcrypt (hopefully that's the name) does the encoding and stores the password as the password hash which is in the model. I think it also said that it then matches the hash to the encrypted password to authenticate.

Hopefully that's useful!


#3

I'm still confused. I'm not sure when or how Rails is encrypting the password or storing it in the database for this exercise. I did see a reference to bcrypt in the Gemfile, but the code they've provided us with doesn't seem to actually be using it anywhere or storing the password in the database at all.

After doing some web searching, I stumbled across a tutorial at SitePoint that uses this code for bcrypt:

before_save :encrypt_password
after_save :clear_password
def encrypt_password
  if password.present?
    self.salt = BCrypt::Engine.generate_salt
    self.encrypted_password= BCrypt::Engine.hash_secret(password, salt)
  end
end
def clear_password
  self.password = nil
end

So, it looks to me like Rails doesn't do this automatically and they left some important details out of the lesson.

Thank you.


#4

So from what I've been able to find, by adding the has_secure_password to the User model in the last (or maybe the one before that) exercise, we enabled what is essentially the auto-hashing of the password whenever it is entered.

So the User model contains
class User < ActiveRecord::Base
has_secure_password
end

The first line draws methods from the parent class ActiveRecord.
The second line adds method has_secure_password

So when there is a password being used it calls on that method which is contained in the ActiveRecord::SecurePassword module (which is part of ActiveRecord::Base). The has_secure_password method contains the code that hashes the password and passes it into the :password_digest attribute and only the hash is stored (password is discarded and never saved). (see more about the ActiveRecord::SecurePassword method here)

It looks like the SitePoint tutorial is literally doing the authentification from scratch. The tutorial doesn't add the has_secure_password method to the model nor does it use the required :password_digest attribute which is required for the has_secure_password method. (see the source code of ActiveRecord::SecurePassword in the link.) The method that is being proposed in that tutorial is an expansion of hashing. It's designed to prevent hash lookup/rainbow/reverse-lookup tables. (Two really good articles about hashes and hash security can be found here and here and I had a third but lost it :frowning: .)

[I think part of the confusion might also be due to the difference between encryption and digesting. Encryption involves having the cleartext password being converted into ciphertext and back, while digesting is a one-way process. The BCrypt algorithm digests the cleartext password into a hash and then Ruby is storing the hash in the app. The cleartext password is discarded and never saved anywhere. When a user logs back into their account, the password is digested and the hash is compared to the one stored in the password_digest column. If there is an exact match, then the user is logged in. This is done so that in the event the server is hacked, there is no database that contains cleartext passwords. Although the hacker could log into the hacked system (using a rainbow or lookup table or a script to match hashes if the encryption is known), they would not have the actual password the user types in. This provides security in case the user has reused the password. The password method in the SitePoint tutorial uses a random salt which will help make it nearly impossible to determine the password that was used to generate a hash. The SitePoint tutorial calls the digest an encryption, which it is technically not.]

Hopefully that kinda answers the when and how the exercise is doing the hashing. It's basically being done behind the scenes through some Gem code that we aren't really seeing. It would definitely be great if they had explained some of this in the lesson!