Authentication in Ruby on Rails 6 (log in, sign up)
Tags: ruby, rails
Published: 2021-05-29 14:23:10

Pretty much any application that you want to make will include some portions that need to be restricted.

Maybe you have certain pages you want to block, or maybe you just want to make sure users can log in to your site and post items.

Whatever it is, making sure that your website or application has the ability to log in and log out is vital.

The good news is, authentication in Ruby on Rails 6 is incredibly easy to implement. You’ll have this up and running in no time.

Getting started with authentication with Rails 6
The first thing you need to do is make sure you have a users model. Go ahead and add the users model to your app by generating a migration, adding a users table, and then running your migrations. When you set up your user model in the migration, make sure it has a column called “password_digest” and it is the type string.
```ruby
class AddPasswordDigestToUsers < ActiveRecord::Migration[6.1]
  def change
      add_column :users, :password_digest, :string
  end
end
```
In the above image, I already have the users table created, so I just needed to add the password_digest column.

Using the bcrypt gem for authentication
Your gem file should have a gem called “bcrypt” that is commented out. Go ahead and uncomment that gem, or add it to your gem file like so:
```ruby
gem 'bcrypt', '~> 3.1.7'
```
We’re not going to get in the specific of the bcrypt gem and how it works because this isn’t an article about cryptography. Just know that this gem is what’s going to power our authentication system.

Go to your user model (user.rb) and add the method has_secure_password. You get this from the bcrypt gem. If your user model doesn’t have any validations or other code, it should look something like this:
```ruby
class User < ApplicationRecord
  has_secure_password
end
```
## Authentication routes
Next, we need to set up our routes so we can create users and sessions in our controllers.

We’re going to add a few routes here. I’ll list the code and then I’ll explain it.
```ruby
get "signup", to: "users#new"
get "login", to: "sessions#new"
post "login", to: "sessions#create"
delete "logout", to: "sessions#destroy"
resources :users, except: [:new]
```
With authentication, we’re working with both a sessions_controller and a users_controller. The sessions paths above are very similar to other CRUD actions you’ve probably created in your application – you have new, create, and destroy.

At the bottom, we have the resource :users to generate the routes. We exclude :new so our signup page is /signup and not /users/new.

The users form and users_controller
These will be similar to other models in your app. My form to create a user looks like this:
````html
<%= form_with(model: @user, local: true) do |f| %>
 
<div class="form-group">
  <%= f.label :username %><br/>
  <%= f.text_field :username, class: "form-control" %>
</div>
 
<div class="form-group">
  <%= f.label :email %><br/>
  <%= f.email_field :email, class: "form-control" %>
</div>
 
<div class="form-group">
  <%= f.label :password %><br/>
  <%= f.password_field :password, class: "form-control" %>
</div>
 
<div class="form-group">
  <%= f.submit %>
</div>
 
<% end %>
```
Nothing should look too out of the ordinary here, except the fact we are using the field :password instead of :password_digest. The has_secure_password method we used earlier gives us the user model the virtual method user.password.

By the way, this form is under views/users/new.html.erb.

In the users_controller, we’re just going to do the same as we would with any other model. Make sure we have our new and create methods.

Initializing the @user in the new method is going to allow us to work with errors when we render our form if the user’s submission isn’t accepted. Here’s the relevant code for new and create.
```ruby
def new
   @user = User.new
 end
 
 def create
   @user = User.new(user_params)
   if @user.save
     flash[:notice] = "User created."
     redirect_to root_path
   else
     render 'new'
   end
 end
 
private
 
 def user_params
   params.require(:user).permit(:username, :email, :password)
 
end
```
Great! We can now create a user, the first step to authentication in rails. Now, let’s get to the good stuff.

The log in form, sessions, and the sessions_controller

Let’s create a form to log in. This is going to go in views/sessions/new.html.erb.
```html
<%= form_with(scope: :session, url: login_path) do |f| %>
 
<div class="form-group">
  <%= f.label :email %><br/>
  <%= f.email_field :email, class: "form-control" %>
</div>
 
<div class="form-group">
  <%= f.label :password %><br/>
  <%= f.password_field :password, class: "form-control" %>
</div>
 
<div class="form-group">
  <%= f.submit "Log in", class: "btn btn-primary" %>
</div>
 
<% end %>
```
This is very similar to your new users form. Here, we’re getting the email address so we can find the user and then getting the password to check against the password they used to login.

Notice we’re using scope: :session, url: login_path here.

Create a sessions_controller and put the following code. I’ll explain what it does.
```ruby
def create
  user = User.find_by(email: params[:session][:email].downcase)
  if user && user.authenticate(params[:session][:password])
    session[:user_id] = user.id
    flash[:notice] = "Logged in successfully."
    redirect_to user
  else
    flash.now[:alert] = "There was something wrong with your login details."
    render 'new'
  end
end
 
def destroy
  session[:user_id] = nil
  flash[:notice] = "You have been logged out."
  redirect_to root_path
end
```
First, we are finding the user by the email they entered in the login in form. You can chose to use something else like username if you want – I just went with email.

Next, we’re checking if that user exists and if the password is correct. The user.authenticate method takes in the password that was submitted on the form. The method then checks this against the hashed password in the database. If the password is correct, the user is returned.

After that, we’re setting a session variable by using session[:user_id] = user.id. Later, on the the app, we’ll check if the session[:user_id] is not equal to nil. If it’s not, that means the user is logged in.

The destroy method logs the user out. As you can see, we’re just toggling session[user_id] to log in and log out.

Now, in order to protect a route, all you have to do is something like this.
```ruby
if session[:id] #means the user is logged in
  # allow the user to do cool stuff
else
  redirect_to login_path
end
```
Because we need to know if the user is logged in a lot we’re going to set up some special helper methods in our application_controller.rb file.
``` ruby
helper_method :current_user, :logged_in?
def current_user
  @current_user ||= User.find(session[:user_id]) if session[:user_id]
end
 
def logged_in?
  !!current_user
end
 
def require_user
  if !logged_in?
    flash[:alert] = "You must be logged in to perform that action."
    redirect_to login_path
  end
end
```
These methods will be used in our controllers and views to check for things like:
```ruby
if logged_in?
  link to logout path
else
  link to login path
end
```
And
```ruby
<h1>Welcome <%=current_user.username %> </h1>
```
If you want to learn more about web development, make sure to [follow me on Twitter](https://twitter.com/heyjoshlee).