UPDATE: I rearranged the order of some of the code snippets to make it easier to scan and find. At the end of the article I go into a bit more detail about what is going on in the snippets, but if any of you have any questions please feel free to ask.
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
# spec/system/sign_up_spec.rb
require 'rails_helper'
describe "User signs up", type: :system do
let(:email) { Faker::Internet.email }
let(:password) { Faker::Internet.password(min_length: 8) }
before do
visit root_path
end
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")
end
end
I'm just going to give you all the code you need to pass that test!
Routes
# 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'
end
Controllers
# app/controllers/home_controller.rb
class HomeController < ApplicationController
def show
end
end
# app/controllers/dashboard_controller.rb
class DashboardController < ApplicationController
def show
end
end
# app/controllers/user_registrations_controller.rb
class UserRegistrationsController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
auto_login(@user)
redirect_to dashboard_path, notice: "You have registered successfully."
else
render :new
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
Views
# app/views/home/show.html.erb
<h1>Homepage</h1>
# app/views/dashboard/show.html.erb
<h1>Dashboard</h1>
# app/views/shared/_navbar.html.erb
<% if current_user %>
<%= current_user.email %>
<% else %>
<%= link_to "Register", register_path %>
<% end %>
# app/views/layout/application.html.erb
<%= render "shared/navbar" %>
<% if notice %><%= notice %><% end %>
<% if alert %><%= alert %><% end %>
# app/views/user_registrations/new.html.erb
<h1>Register an account</h1>
<%= form_with(model: @user, url: user_registration_path, method: :post) do |form| %>
<div>
<%= form.label :email %>
<%= form.text_field :email %>
</div>
<div>
<%= form.label :password %>
<%= form.password_field :password %>
</div>
<div>
<%= form.label :password_confirmation %>
<%= form.password_field :password_confirmation %>
</div>
<div>
<%= form.submit "Sign up" %>
</div>
<% end %>
Models
# app/model/user.rb
class User < ApplicationRecord
authenticates_with_sorcery!
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
end
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.
Top comments (0)