The very first thing you will have to do after setting up your new Rails project is go to your Gemfile and add the “BCrypt” Gem. Often all you will have to do is ‘uncomment’ it out. In short, what BCrypt does is make it much harder for a hacker to run whats called a “Rainbow Table” against your database. BCrypt encrypts your passwords and takes it one step further by “Salting” those passwords. Salting is essentially adding random strings on top of your already encrypted password making it even harder to break (even with a Rainbow Table)
Now run: “bundle install” in. your terminal to install BCrypt.
The next step is quite easy. Since you are setting up a backend for users to login, it would only make sense to have a User model in addition to any other models that you may have in your database. So in this step create your User model and controller using the “rails generate Resource” command. In the example below I only chose to have two columns in my database table. You may have additional columns. Ensure you include those columns when you generate your models. You may also choose to use the rails scaffold command to generate models and more.
It is very important to plan the structure of your database tables ahead of time (that is another topic in itself) to avoid having to add columns to your tables. And while its not a huge deal to add a column, not having to add stuff you forgot or did not plan for in advanced only slows you down and adds more complexity to your project. So assuming you have set up all your models and controllers correctly, it is now time to create your database and run migrations. I am using postgresql for my databases so in order for me to run “rails db:migrate” command, I must first run “rails db:create”. Then I can run rails db:migrate. If you are running sqlite3 all you need to do is run “rails db:migrate” and it will create and run the migrations in one step. After you create your database, you should check your schema file to ensure all your tables are set up correctly and ensure that you have “password_digest” as a column in your Users table. Password_diget should be set to a string.
A mistake often made when learning this process is to put a password column as well as a password_confirmation column in your User model, and not creating one for password_digest. If you find you have made that mistake you can run “rails db:rollback”, “rails db:drop” and then delete a column and add the column you forgot and then re-migrate the database.
So now your models, should be set up correctly and database created. first open up your User model and add “has_secure_password” at the top of your User model. It is very important you have this here or BCrypt will not work. Having “has_secure_password” allows you to have two new instance methods in your user model: “password” and “password_confirmation”. These are the two params your User on the front end will be sending to the backend via a form you set up for signup and login.
At this point I like to do two additional things in the backend. If you think about your User’s experience, at one point we will want to ensure a user can maintain a session went they log in and that we can send a cookie to the client browser so that they can auto login at some point in the future (sort of like when you go to Amazon.com and your login is handled automatically). To enable this on your backend first go to your “config/application.rb” and add the following 3 lines of code.
Next got to your “applications controller and add the following line of code.
With the code we just added to these two files, we can now have the ability to add a cookie to the users browser and maintain a session when a user logs in.
At this point we need to do a few more things that I am not going to go into great detail about. Im assuming that you have or will code some sort of sign-up / login form on your front end. The signup form will need to have a username input, password input, a password_confirmation input and a submit button that onClick will make a POST request to your Users controller send the information on your 3 inputs to the backend. I highly recommend using a console log off the fetch to see the data that is returned. If done correctly you will console.log a user object and you will know that your user is being created. (wait until after we code the sessions controller (below) to begin console.logging anything). If you used the “rails g resource” or “rails g scaffold” command mentioned earlier, rails already set this route for you in your config/routes.rb file.
Now go to your User controller and code your create action like this…
A few word of warning. In this action I have already created a private action (not pictured) that finds my user params. I also have customized my render to return movies (your application may differ). The thing you must render to the front end is “user”. I also included an errors message in the else statement for those who are not using an Active Record Rescue. I want to point out the exclamation after the User.create!. Should there be an error in the creation of a user, this exclamation raises an Active Record flag and generates an error for you. But only if you’ve coded the rescue either in the private area of your controller or in your application controller. I wanted to show users both methods. So choose one method and stick with it. I highly recommend using a rescue in the application controller and just taking out the else statement. It will look much cleaner.
Next we will need to make a Sessions controller. Utilize the “rails g controller Sessions” command.
You will need write custom routes in your /config/routes.rb file to handle your login and auto login. Again, Im going to assume you’ve already created an front end with a form for login that has an input for username and password with a submit button that onClick makes a POST request to the custom endpoint we just defined called /login (see below)
After you have created the sessions controller, code a create action that looks like this…
What this action is doing first is finding the user using the params passed to it from the front end. It is using the username param from the username input you should have already created on the frontend login page. Then it checks for a condition. If its is the user and password matches (the authenticate method) then set session to the user.id and return a user object to the frontend. If not, render error message unauthorized.
If everything is coded correctly you should be rendering a user object to the front end. If you console.log the response data in the front end, you should see and object ‘user’ in the browser console. As with coding there is a chance you may not be console logging anything. You may need to adjust your code. Just go through systematically, starting from the submit button. Then follow the flow of information starting with where the information reaches the backend. It first reaches the routes.rb, then goes to the controller.
Pro tip: Put a ‘byebug’ in your action and make a request to that action from the frontend. If it hits your ‘byebug’, type “params” in the terminal and see if params are coming through correctly. If not, then there is a problem with your frontend most likely. If you see the params in the terminal, read it carefully and begin trouble shooting why the params are not creating a new user or not logging in correctly. Little by little eliminate one possibility after another, until finally you are left with one or two possibilities.
Next steps….If you are coding in React you will need to begin setting up functions to handle state and check for session ID on page load for your auto login feature (not covered in this article). This article is intended to help others with setting up a very basic Rails sign-up and login. Of course from here it can also get even more complicated.
I hope this has been helpful to those just learning Auth for the first time. It can be confusing at first, but if you do it a few times, it will start to become easier and easier. If you found this article helpful, please like and subscribe to the blog. Thank your for taking the time to read this blog post!