In this tutorial, we'll dive into rolling our own authentication in Rails, allowing users to sign in and log out. We'll be using the has_secure_password library that comes built-in with Rails, providing security by handling encryption and creating secure passwords.

While other authentication libraries like Devise or Clearance offer more features (e.g., forgot your password, confirm your account, etc.), rolling your own authentication can be faster and give you complete control over your app's login process. In an upcoming video, we'll also explore extending this functionality using GraphQL.

Starting with the application controller

First, let's add some helper methods to our ApplicationController:

class ApplicationController < ActionController::Base
  private

  def ensure_signed_in!
    redirect_to root_path unless current_account
  end

  def current_account
    if session[:account_id]
      Account.find_by(id: session[:account_id])
    end
  end
  helper_method :current_account

  def sign_in(account)
    session[:account_id] = account.id
  end

  def sign_out
    session[:account_id] = nil
  end
end

current_account loads the current account from an Account with a matching session ID. We then make this method available in our views with helper_method :current_account. sign_in and sign_out take care of setting and clearing the session. Finally, ensure_signed_in! redirects the user to the root path if they're not signed in.

Implementing registration and login

With these helper methods in place, we can now add sign-in functionality to our registration process. In our RegistrationController, let's call sign_in after a successful registration:

class RegistrationController < ApplicationController
  def create
    @account = Account.new(account_params)

    if @account.save
      sign_in(@account)
      redirect_to signup_success_path
    else
      render :new, status: :unprocessable_entity
    end
  end
end

Now, we need to create a SessionsController to handle both signing in and signing out:

class SessionsController < ApplicationController
  def new
  end

  def create
    account = Account.find_by(email: params[:email])
    if account&.authenticate(params[:password])
      sign_in(account)
      redirect_to signup_success_path
    else
      flash[:signin] = "Invalid email or password"
      render :new, status: :unprocessable_entity
    end
  end

  def destroy
    sign_out
    redirect_to root_path
  end
end

Our create action finds the account by email and authenticates the password. If successful, the account is signed in and redirected to the signup_success_path. If not, an error message is displayed. The destroy action simply signs the user out and redirects to the root path.

Update the config/routes.rb file to include new routes for sessions:

Rails.application.routes.draw do
  resources :registrations, only: %i[new create]
  resources :sessions, only: %i[new create]
  get "/logout", to: "sessions#destroy"
end

Creating the login form

Now, let's create a view for sessions with a simple login form:

<h1 class="font-bold text-2xl text-slate-700 text-center">Sign in</h1>

<p class="text-center mt-1">
  Need an account? <%= link_to "Register", new_registration_path, class: "text-blue-600 underline" %>
</p>

<%= form_with(url: sessions_path, class: "mt-8") do |f| %>
  <div class="mb-4">
    <%= f.email_field :email, placeholder: "Email address", class: input_class %>
  </div>
  <div class="mb-4">
    <%= f.password_field :password, placeholder: "Password", class: input_class %>
  </div>

  <% if flash[:signin] %>
    <div class="mb-4 bg-pink-100 text-pink-700 p-3 rounded-md text-center">
      <%= flash[:signin] %>
    </div>
  <% end %>

  <%= f.button type: "submit", class: button_class do %>
    Login
  <% end %>
<% end %>

To finish things off, treat yourself with some tailwind CSS styling or any other way of your choice, and tweak your app's layout to include both login and logout links.

At this point, our app has a basic rolling authentication for signing up, logging in, and logging out. In the next tutorial, we'll expand on this by setting up GraphQL Ruby with mutations for register and login, and a query for fetching current account details. This will give you a solid foundation for setting up a GraphQL server in your future projects.

Discussion (0)

To comment you need to sign up for a free account or sign in.

Get new videos in your inbox weekly!

Be the first to hear about new courses and videos launching. You’ll only receive relevant news, no spam.