Simple Rails authentication
Related videos
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.