Rails 8 includes an authentication generator, so a new application no longer needs a gem just to implement email-and-password sign-in. The generator provides sign-in, sign-out, password reset, session tracking, and the code that protects controllers by default.
It deliberately does not generate user registration. Sign-up requirements vary between applications, but a basic registration flow only needs a route, controller, view, and a few user validations. This article uses Rails 8.1 and shows how to add them.
Create the application
Rails 8.1 requires Ruby 3.2 or newer. If Rails is not installed, install the current 8.1 release and create an application:
gem install rails -v "~> 8.1"
rails new rails8-auth
cd rails8-auth
If you are adding authentication to an existing application, commit your work first. The generator creates and modifies several files, and an existing users table will need to be reconciled with its migration before you run it.
Generate authentication
Run the authentication generator and migrate the database:
bin/rails generate authentication
bin/rails db:migrate
The generator adds bcrypt and creates, among other files:
User,Session, andCurrentmodelsSessionsControllerfor sign-in and sign-outPasswordsControllerandPasswordsMailerfor password resets- an
Authenticationcontroller concern - session and password-reset views
usersandsessionsmigrations- session and password routes
The generated User uses has_secure_password. Sessions are persisted in the database, while a signed, HTTP-only cookie identifies the current session. Current.session stores the session for the request and Current.user delegates to its user.
ApplicationController includes the generated concern, which means authentication is required by default. Mark public actions with allow_unauthenticated_access. The concern also exposes authenticated? to views and remembers the originally requested URL when it redirects a visitor to sign in.
The generated SessionsController rate-limits sign-in attempts to 10 requests per three minutes. Treat this as a useful baseline rather than complete protection against distributed attacks.
You can inspect all of this code in your application. Unlike an authentication library hidden behind an API, the generated files belong to the application and can be adapted to its requirements.
Add a root route
The generated authentication concern falls back to root_url after a successful sign-in. Generate a simple home page if the application does not already have one:
bin/rails generate controller Home index --skip-routes
Add its root route and the registration route to config/routes.rb:
Rails.application.routes.draw do
resource :session
resources :passwords, param: :token
resource :registration, only: %i[new create]
root "home#index"
end
Keep any other generated routes in the file as well.
Add registration validations
The authentication generator normalizes email addresses and creates a unique database index, but it does not validate email format. Add validations to app/models/user.rb so the registration form can show useful errors:
class User < ApplicationRecord
has_secure_password
has_many :sessions, dependent: :destroy
normalizes :email_address, with: ->(email) { email.strip.downcase }
validates :email_address,
presence: true,
uniqueness: true,
format: { with: URI::MailTo::EMAIL_REGEXP }
end
The model-level uniqueness validation improves form feedback. The generator’s unique database index remains necessary to prevent duplicate addresses when concurrent requests race.
has_secure_password validates that a password is present when a user is created, limits it to 72 bytes, and supports a password_confirmation attribute. It does not impose a minimum length or complexity policy, so add one if the application requires it.
Create the registration controller
Create app/controllers/registrations_controller.rb:
class RegistrationsController < ApplicationController
allow_unauthenticated_access only: %i[new create]
rate_limit to: 10, within: 3.minutes, only: :create,
with: -> { redirect_to new_registration_path, alert: "Try again later." }
def new
@user = User.new
end
def create
@user = User.new(registration_params)
if @user.save
start_new_session_for @user
redirect_to root_path, notice: "Welcome! Your account has been created."
else
render :new, status: :unprocessable_content
end
end
private
def registration_params
params.require(:user).permit(
:email_address,
:password,
:password_confirmation
)
end
end
The two actions must allow unauthenticated access because a visitor does not have a session yet. After a successful save, start_new_session_for uses the same generated session code as a normal sign-in. Returning HTTP 422 when validation fails lets Turbo render the invalid form correctly.
Create the registration view
Create app/views/registrations/new.html.erb:
<h1>Create an account</h1>
<%= form_with model: @user, url: registration_path do |form| %>
<% if @user.errors.any? %>
<div role="alert">
<h2><%= pluralize(@user.errors.count, "error") %> prevented registration:</h2>
<ul>
<% @user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= form.label :email_address %>
<%= form.email_field :email_address,
required: true,
autofocus: true,
autocomplete: "username" %>
</div>
<div>
<%= form.label :password %>
<%= form.password_field :password,
required: true,
autocomplete: "new-password",
maxlength: 72 %>
</div>
<div>
<%= form.label :password_confirmation %>
<%= form.password_field :password_confirmation,
required: true,
autocomplete: "new-password",
maxlength: 72 %>
</div>
<%= form.submit "Create account" %>
<% end %>
<%= link_to "Already have an account? Sign in", new_session_path %>
You can also add a link to new_registration_path in the generated sign-in view.
Start the server and visit /registration/new:
bin/rails server
Before using this in production
This is intentionally a basic sign-up flow. Depending on the application, consider adding:
- email-address confirmation with expiring, single-use tokens
- invitations or an administrator approval step
- stronger password rules and compromised-password checks
- bot protection and additional registration throttling
- multi-factor authentication
- an account recovery policy
- audit logging and tools for reviewing active sessions
Do not describe an address as verified merely because the user registered with it. Email confirmation is a separate feature and should account for token expiry, resending, address changes, and existing users.
The Rails security guide documents the generated authentication flow and further security considerations. Review the generated code and adapt it to the risks and requirements of your application.