Skip to content

Adding Sign Up to the Rails 8 Authentication Generator

This article spurned from writing about the new Rails 8 Authentication Generator in my Build A SaaS App in Ruby on Rails 8 book, and I wanted to share this small tip from the chapter on Users and Authentication.

Within the book, I set up a Rails 8 application with authentication using the bin/rails generate authentication command. This command creates a User model, a Sessions controller, and a few views to get you started. However, it does not include a Sign-up process at all.

In this quick article, we will add a Sign-Up form to the application.

Prerequisites

Before we start, ensure you have the Rails 8 application set up with the authentication generator.

Terminal window
rails new myapp --main
cd myapp
bin/rails generate authentication
bin/rails db:migrate

Adding Sign Up

To add a Sign-Up form, we will need to add a few things:

  1. A route to the Sign Up form
  2. A controller action to render the Sign-Up form
  3. A view to display the Sign Up form
  4. A controller action to handle the form submission

Route

In the config/routes.rb file, add a route to the Sign Up form in addition to the routes created by the authentication generator:

config/routes.rb
Rails.application.routes.draw do
resource :session
resource :registration, only: %i[new create]
resources :passwords, param: :token
end

Controller

Create a new controller called RegistrationsController:

app/controllers/registrations_controller.rb
class RegistrationsController < ApplicationController
allow_unauthenticated_access
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
start_new_session_for @user
redirect_to root_path, notice: 'Successfully signed up!'
else
render :new
end
end
private
def user_params
params.require(:user).permit(:email_address, :password, :password_confirmation)
end
end

It’s pretty standard stuff here. First, allow_unauthenticated_access is a method that allows unauthenticated access to the new and create actions. Otherwise, we could use this controller with a valid session. The only other thing that is not standard Rails controller CRUD is the start_new_session_for @user, which allows the app to be signed in (add session record, secure cookie, etc.) after the user is created.

View

Create a new view file called new.html.erb in the app/views/registrations directory (view assumes Tailwind was added and has some classes):

app/views/registrations/new.html.erb
<div class="mx-auto md:w-2/3 w-full">
<% if alert = flash[:alert] %>
<p class="py-2 px-3 bg-red-50 mb-5 text-red-500 font-medium rounded-lg inline-block" id="alert"><%= alert %></p>
<% end %>
<% if notice = flash[:notice] %>
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
<% end %>
<h1 class="font-bold text-4xl">Sign up</h1>
<%= form_with model: @user, url: registration_url, class: "contents" do |form| %>
<div class="my-5">
<%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address], class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
</div>
<div class="my-5">
<%= form.password_field :password, required: true, autocomplete: "current-password", placeholder: "Enter your password", maxlength: 72, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
</div>
<div class="my-5">
<%= form.password_field :password_confirmation, required: true, autocomplete: "current-password", placeholder: "Confirm your password", maxlength: 72, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
</div>
<div class="col-span-6 sm:flex sm:items-center sm:gap-4">
<div class="inline">
<%= form.submit "Sign up", class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
</div>
<div class="mt-4 text-sm text-gray-500 sm:mt-0">
<%= link_to "Forgot password?", new_password_path, class: "text-gray-700 underline" %>
</div>
</div>
<% end %>
</div>

Form Submission

If you were to try to submit the form now, it would work, but redirect to the registration form as there is no root path or authenticated path to test this all out on. Let’s create a quick controller and route to handle this:

app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
render plain: "Welcome to the app!"
end
end
config/routes.rb
Rails.application.routes.draw do
get 'home/index'
root to: 'home#index'
resource :session
resource :registration, only: %i[new create]
resources :passwords, param: :token
end

If you visit the root path of your application, you should see the Sign-Up form. You can fill it out and submit it to create a new user. Upon successful creation, you should be redirected to the root path with the message “Welcome to the app!”

Bonus Idea

Usually, when building an app, there is a different layout when you are logged in than when you are on one of the “authentication adjacent” pages. You can add a method to the ApplicationController to determine if the user is authenticated or not:

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_layout
def set_layout
@layout = user_signed_in? ? 'application' : 'authentication'
end
end

Then, in your app/views/layouts directory, create two layout files: application.html.erb and authentication.html.erb. In the application.html.erb file, you can add a navigation bar, footer, etc. You can add a different navigation bar, footer, etc., in the authentication.html.erb file.