Custom authentication flow with Sorcery gem RSpec and Rails 7 part 1

We’re going to install Sorcery, a low-level authentication gem to help you create a custom authentication flow for your app

I’m picking up where I left off in a post where I install RSpec onto a fresh Rails 7 app. If you want to start there so we are on the same page please feel free: Install RSpec on Rails 7

git checkout -b sorcery-sign-up-feature
bundle add sorcery
bundle install
rails g sorcery:install
rails db:migrate

Before we write any code let’s just write a simple spec to serve as our guide so we know what to focus on energy on.


# spec/system/sign_up_spec.rb

require 'rails_helper'

describe "User signs up", type: :system do
  let(:email) { }
  let(:password) { Faker::Internet.password(min_length: 8) }

  before do
    visit root_path

  scenario "register user account" do
    click_on "Register"
    fill_in "Email", with: email
    fill_in "Password", with: password
    fill_in "Password confirmation", with: password
    click_button "Sign up"

    expect(page).to have_content("Dashboard")
    expect(page).to have_content(email)
    expect(page).to have_no_content("Register")

I’m just going to give you all the code you need to pass that test!


# config/routes.rb

Rails.application.routes.draw do
  get 'dashboard', to: 'dashboard#show'
  get 'register', to: 'user_registration#new'
  resources :user_registrations, only: :create
  root 'home#show'


# app/controllers/home_controller.rb

class HomeController < ApplicationController
  def show
# app/controllers/dashboard_controller.rb

class DashboardController < ApplicationController
  def show
# app/controllers/user_registrations_controller.rb

class UserRegistrationsController < ApplicationController
  def new
    @user =

  def create
    @user =
      redirect_to dashboard_path, notice: "You have registered successfully."
      render :new

  def user_params
    params.require(:user).permit(:email, :password, :password_confirmation)


# app/views/home/show.html.erb

# app/views/dashboard/show.html.erb

# app/views/shared/_navbar.html.erb

<% if current_user %>
  <%= %>
<% else %>
  <%= link_to "Register", register_path %>
<% end %>
# app/views/layout/application.html.erb

<%= render "shared/navbar" %>
  <p id="notice"><%= flash[:notice] %></p>
  <p id="alert"><%= flash[:alert] %></p>
# app/views/user_registrations/new.html.erb

<h1>Register an account</h1>

<%= form_with(model: @user, url: user_registration_path, method: :post) do |form| %>
    <%= form.label :email %>
    <%= form.text_field :email %>

    <%= form.label :password %>
    <%= form.password_field :password %>

    <%= form.label :password_confirmation %>
    <%= form.password_field :password_confirmation %>

    <%= form.submit "Sign up" %>
<% end %>


# app/model/user.rb

class User < ApplicationRecord

  validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] }
  validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] }
  validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] }

  validates :email, uniqueness: true

Let’s finish up

git status
git add -A
git commit -m "add sorcery sign up and auto login flow"
git checkout main
git merge sorcery-sign-up-feature
git branch -D sorcery-sign-up-feature
git push

Continue reading for my ramblings

By the way I just wanted to point out that sweet auto_login helper method Sorcery has to automatically log in your user. We slipped it into the create action to create a nice seamless way to log in a user. Sorcery has the option for user activation via a confirmation link as well if you wish to incorporate that.

Next time we will create a way to sign out and actually hide pages from users not signed in. Right now you can see the dashboard page whether you are logged in or not but in our next test we’ll focus on that. We’ll also add a way to sign out, delete users, sign in, and show form errors. The form we made only works for registering accounts, not signing in existing ones. If you try using the form again you’ll see in the server console that it doesn’t let you move forward with the action because we set a validates uniqueness on email attributes.

Speaking of attributes, you might have noticed the migration file Sorcery generated only had 3 attributes: email, salt, and crypted_password. If you are unsure about how we were able to fill in password and password_confirmation through the form then you might not be alone. This was done with something called “virtual attributes”. The form attributes had values but they were not persisted into the database, the password the user wrote was encrypted and that was stored into the database. You never want to store plain passwords in a database. No-no.

Hope you enjoyed the tutorial! This is the great thing about Sorcery, you are in control of your authentication flow and can add or remove as much or little as you like.