Don't forget to use Git to save your projects!
In these projects, you'll be working to implement authentication systems so users can only access areas of a site they are authorized to.
We're starting to get into the meaty stuff with the tutorial. Take your time and pay attention to which file (especially for the specs) you're supposed to be writing in. A common and frustrating mistake is to put your
describe block in the wrong place.
You'll implement signin and signout functionality for the user, which opens the door to allow more destructive user behaviors like editing and deleting things (which should only be allowed for a given user or admin). In Chapter 10, you'll get the chance to implement these functions.
In this project, you'll be building an exclusive clubhouse where your members can write embarrassing posts about non-members. Inside the clubhouse, members can see who the author of a post is but, outside, they can only see the story and wonder who wrote it.
This will be a chance for you to "roll your own" authentication system, very similar to how you did in the tutorial. As usual, we will be focusing on data and function, not style. If you want to add your own stylistic flourishes, consider it extra credit.
It's easy to feel a bit overwhelmed by building your own authentication. That's because there are several moving parts -- the session controller/form, hanging onto and refreshing the remember token when necessary, and using that token to check up on the current user. It may help if you write out the steps as you understand them prior to getting started, so you know what you're shaky on and will need to pay attention to.
The projects will be less and less explicit about how to achieve their goals, since we expect you to build on your previous knowledge. If you don't know how to do something, feel free to check back in previous lessons or projects or Google the correct way to implement it (though be careful, because that may take you deeper down the road than we intended). The Ruby on Rails Tutorial will be a good reference.
Up until now, we've been treating the "password" field of user submissions pretty cavalierly... it's mostly been an afterthought included for the sake of completeness. We certainly haven't been storing them properly or encrypting them, which we should. So wipe all that stuff from your memory because now you've got the tools to properly encrypt user passwords and we'll be doing it from now on. Actually, you've had the tools since back when you did chapter 6 of the Ruby on Rails Tutorial.
If you'd like to challenge yourself, don't even follow the steps below, just go ahead and build the app!
:password_digestfield instead of a
members-onlyRails app and Github repo. Update your README.
bcrypt-rubygem in your Gemfile.
$ bundle installit. (note: This might just be
#has_secure_passwordmethod to your User file.
User.create(:name => "foobar", :email => "firstname.lastname@example.org", :password => "foobar", :password_confirmation => "foobar")
#authenticate command which is now available on your User model (thanks to
#has_secure_password) on the command line -- does it return the user if you give it the correct password?
> user = User.create(:name => "foobar", :email => "email@example.com", :password => "foobar", :password_confirmation => "foobar") > user.authenticate("somethingelse") => false > user.authenticate("foobar") => #<User id: 1, name: "foobar", email: "firstname.lastname@example.org", password_digest: "$2a$10$9Lx...", created_at: "2016...", updated_at: "2016...">
Now let's make sure our users can sign in.
sessions_controller.rband the corresponding routes. Make "sign in" links in the layout as necessary.
#newaction to create a blank session and send it to the view.
#form_forto sign in the user at
app/views/sessions/new.html.erb. Verify that you can see the form.
:remember_tokenwhich will store that user's special token.
When you create a new user, you'll want to give that user a brand new token. Use a
#before_create callback on your User model to:
SecureRandom.urlsafe_base64to generate a random string)
Digest::SHA1.hexdigestmethod on the stringified (
#to_s) version of your token)
Create a couple of users to populate your app with. We won't be building a sign up form, so you'll need to create new users via the command line. Your
#before_create should now properly give each newly created user a special token.
Now fill in the
#create action of your SessionsController to actually create the user's session. The first step is to find the user based on their email address and then compare the hash of the password they submitted in the params to the hashed password stored in the database (using
#authenticate). See Chapter 8 with questions but try not to immediately copy verbatim -- you're doing this to learn.
Once you've verified that the user has submitted the proper password, sign that user in.
Create a new method in your ApplicationController which performs this sign in for you. Give the user a new remember token (so they don't get stolen or stale). Store the remember token in the user's browser using a cookie so whenever they visit a new page, we can check whether they are signed in or not. Use the
cookies.permanent "hash" to do this.
Create two other helpful methods in your ApplicationController -- one to retrieve your current user (
#current_user) and another to set it (
#current_user=(user)). Retrieving your current user should use the
||= operator -- if the current user is already set, you should return that user, otherwise you'll need to pull out the remember token from the cookie and search for it in your database to pull up the corresponding user. If you can't find a current_user, return
Set your current user whenever a user signs in.
Build sign out functionality in your
SessionsController#delete action which removes the current user and deletes the remember token from the cookie. It's best if you make a call to a method (e.g.
#sign_out) in your ApplicationController instead of just writing all the functionality inside the SessionsController.
Create a link for signing out which calls the
#delete method of your session controller. You'll need to spoof the DELETE HTTP method, but that's easily done by passing
#link_to the option
:method => :delete.
Authentication and Posts
Let's build those secrets! We'll need to make sure only signed in users can see the author of each post. We're not going to worry about editing or deleting posts.
[:new, :create, :index]methods.
#before_filterto restrict access to the
#createmethods to only users who are signed in. Create the necessary helper methods in your ApplicationController.
app/views/posts/new.html.erbview which will create a new Post.
#createaction build a post where the foreign key for the author (e.g.
user_id) is automatically populated based on whichever user is signed in. Redirect to the Index view if successful.
#indexaction of the PostsController and create the corresponding view. The view should show a list of every post.
This is obviously a somewhat incomplete solution... We currently need to create new users from the Rails console. But it should give you some practice figuring out authentication. Feel free to improve it... maybe you'll be the next SnapChat.
Send us your solution so we can show others! Submit a link to the Github repo with your files in it here using any of the methods listed on the contributing page. Please include your partner's github handle somewhere in the description if they would like attribution.
This section contains helpful links to other content. It isn't required, so consider it supplemental for if you need to dive deeper into something
From the creators of The Odin Project...