Authentication in Ruby onRails — Sign-Up, Log-in and Log-out

CG_Musta
8 min readMar 4, 2020

--

Sign-Up

Most of the website gives the user the ability to Log-in or Log-out; we are going to see step by step how to build di functionality in ruby.

Let say that you go on the website www.somethig.com

Your browser, ‘clients ’, will send a GET request to the server and hits the /signup route.

The router will look for the controller ‘UsersController’ and the action new; the action passes the request st to the viewer to display the signup form to create a new User.

When the user fills the form and hit the submit button, the routes will send a Post request to create an action in the UsersController.

The method “create” save the data in the database, and redirect the user to the main page or the user page, this is up to you where would you like to redirect the user after the action has been processed, and initiate a new session.

A session is a connection between the server which runs the rails applicational and the user’s computer.

A session starts whenever the User Login and end when the User Logout.

Let’s create the logic step by step:

First, go to the model User app/models/user.rb, and add this line:

class User < ActiveRecord::Basehas_secure_passwordend

The has_secure_password method allows to securely save the password.

The algorithm used by this method is called bcrypt which is a gem we add to our Gemfile

In the Gemfile add:

gem 'bcrypt'

and install the gems, by typing in the terminal, bundle install.

Now in the migration file, we need to add the user’s table column.

It Is up to you which info you want to add, but the most important are

t.string: name, t.string: email, and the column for the password, t.string: password_digest.

The password_digest allows using the method we added before to save the password in an encrypted way, without this our password will be saved as a string, and this is not the most secure way to protect a password.

Once the Model user the table and migration are done we need to go into the routes file and add this line:

get 'signup'  => 'users#new'resources :users

Next in the UserController, we need to add our method new:

def new@user = User.newend

Next to create the view in the file, app/views/users/new.html.erb,

Inside the file we need to create our form, we will use Form_for

<div class="login"><div class="container"><div class="form"><h1>Sign up</h1><%= form_for(@user) do |f| %><%= f.text_field :name, :placeholder => "name" %><%= f.email_field :email, :placeholder => "Email" %><%= f.password_field :password, :placeholder => "Password" %><%= f.submit "Create an account", class: "btn-submit" %><% end %></div></div></div>

Ok so now if the user visits the website URL /signup, the browser will make a get request to the UserController with the action new, which will return a view with the form.

Now in the controller, we need to handle the data we are getting from the form and send it to the database; to do this, we need to allow the params we are receiving.

Add a private method in the UsersController:

privatedef user_paramsparams.require(:user).permit(:name, :email, :password)end

This method allows our create method, to receive and use these params we are sending through the form.

Let’s break down the create method and see what’s happening there.

We are receiving the params with the information from the form, and we are assigning to an instance variable @user a new User instance and passing the info using the user_params as argument which is the method we have built to handle the params.

The if statement checks if the user was saved correctly and initiate a new session, The session is stored in a key/value pair, which take the instance variable created @user.id and assigning it to the key:user_id in the session.

We are then redirecting the user to the home page since this method does not have a template in the view, but it is just created to handle the request from the form, we need to specify where we want the user to go after this request has been processed.

In case there is a non-valid action, and the new User instance is not appropriately saved, we will redirect the user to the signup routes again.

Good!! Now when the user hit the button submit, the data will be sent with a POST request to the Rails App. This POST request will run the create method in our controller that will handle the request and redirect the user to a new view and create a new session.

To check if the data was created correctly, we can go in the console (rails console/ or c) and call (User.all).

Now that the user can create a new account, we need to have a method that allows him to login and logout.

Login

We need to create a route for a /login request; in this case, the router will scan the URL/login and send it to the controller Sessions with the action new. Since the user is already created, we not using the new action of the user, but we need to create a new session to hold the info of the user until the logout process.

The new action view will display the new form in the login page; the form will be in the app/views/sessions/new, similar to the new form in the user controller and user views which handle the signup form.

When the form is filled with the data from the user, a POST request will b sent to the create action in the sessions controller, similar to the create action in the user controller, but in this case, we are not creating a user but a session. Let’s create the logic step by step:

In the route file, we need to add the route that maps the /login request, to the session controller and action new.

get '/login' => 'sessions#new'

In SessionController we need to add the method/action new

def new@user = User.newend

Next, we need the form that will be displayed to the user to sign-in, in the views:

app/views/sessions/new.html.erb

<%= form_for(:session, url: login_path) do |f| %><%= f.email_field :email, :placeholder => "Email" %><%= f.password_field :password, :placeholder => "Password" %><%= f.submit "Log in", class: "btn-submit" %><% end %>

Now when a user visits the URL /log in the clients/browser will make a get request which will hit the controller sessions and action new and send back from the view the new form for the login.

Why the form is different??

Well in the form_for we send for the signup we had the model @user this is why the form was form_for(@user). In the session, we do not have a model, and we used the parameters referring to the name of the resource and corresponding URL.

Now we need to create the new session, in the app/config/routes we need to add:

post 'login' => 'sessions#create'

In the controller, we need to add the create method/action:

def create@user = User.find_by_email(params[:session][:email])if @user && @user.authenticate(params[:session][:password])session[:user_id] = @user.idredirect_to '/'elseredirect_to 'login'endend

Good!!, let’s break down this method and see what’s happening here.

When the user sends the data through the sign-in from use the POST request and sends the info to the Rail application.

This request hits the sessions controller and action method.

The create method assign o the instance variable @user the current user details by searching the user by email.

It then checks if the email and the password are matching through an if statement and authenticate the password, with the authenticate method.

If it is matching, he assigns the instance variable if to the session key:user_id.

And redirect the user to the home page else he will redirect him to the login page again if the information provided where wrong.

Log-out

So far, our app allows the user to sing up and log in, but we also need to be able to logout or end the sessions.

In the route file, we need to add the route log out which render to the sessions controller and destroy action/method.

delete 'logout' => 'sessions#destroy'

In the session controller, we need then to add the action destroy:

def destroysession[:user_id] = nilredirect_to '/'end

or

def destroysession.delete :user_idredirect_to '/'end

Now when the browser sends the request to the URL /logout, this will send a delete request to the sessions with the action/method destroy.

The method destroy will delete the session or make it equal to nil, in both cases works

And redirect the user to the homepage.

GOOD JOB!!! Now we have built an entire authentication system.

However, one more thing missing, on the home page a user that is not log in or a non-signup user can still see the content, and we want just user allowed to access to the content in that page.

In this case, we need to be able to check If the user is logged-in, or not before to render the index page content.

Authenticate if a user is logged in or not

In the app/controllers/application_controller.rb, we need to add, a method called current_user:

helper_method :current_userdef current_user@current_user ||= User.find(session[:user_id]) if session[:user_id]end

This method assigns to the instance variable @current_user the session of that user by searching in the sessions if the session exists, another way can be:

def current_user@current_user = (User.find_by(id: session[:user_id]) || User.new)end

Under this method we need another one:

def require_userredirect_to '/login' unless current_userend

The first method current_user checks if the user is logged in or not, in case the sessions exist he will assign it to the instance variable @current_user, in the case does not exist the value of the @curren_user will be nil, and the user will be logged out.

The helper_method allows the current_user method to be available in the rest of the controller and the views.

All the method in the ApplicationController will be by default available in all controllers.

The Second method redirects the user to the login page if the current_user is false

Now that the current_user method is available in the rest of the controller we can go in the controller who handles our index request, let’s say we have a PostController, we need to add this line:

before_action :require_user, only: [:index, :show]

This will call the require_user method before to run, index or show method.

Now in the application layout, we can add the following code.

App/views/layouts/application.html.erb

<% if current_user %><ul><li><%= current_user.email %></li><li><%= link_to "Log out", logout_path, method: "delete" %></li></ul><% else %><ul><li><%= link_to "Login", 'login' %></a></li><li><%= link_to "Signup", 'signup' %></a></li></ul><% end %>

This will check if the current user is logged in and show him the logout option which will send a delete request to the logout_path.

Else

He will display the login or Sign Up option.

And now your full authentication is built and ready to work.

--

--