Rails 8 authentication generator
Rails 8 has introduced a generator
for adding basic authentication code to Rails apps.
This builds upon the authentication primitives
like has_secure_password
that were introduced in previous versions of Rails.
Last week, I decided to create a new Rails app
and try out this new generator.
Running the generator
The first step to add authentication to your app is to run the generator:
bin/rails generate authentication
If you’re used to another Rails authentication generator, like authentication-zero, you’ll notice that there are no bells and whistles here. Just a single generator command, with no additional options.
The generated code
If you want to understand this code,
the best place to start is
the Authentication
module.
It handles tasks such as
writing the current session to a cookie,
looking up the current user from the session, etc.
Other than that,
I would recommend taking a look
at the generated migration
which shows the database schema
for the User
and Session
models,
and also the sessions and passwords controllers.
There’s an excellent walkthrough of the code by BigBinary that does a great job showing each of these pieces in detail.
Adding registrations to Rails 8
The first thing that stood out at this point is that there’s no signup page! The generator only adds login and passwords controllers. This seems like a rather strange choice, considering most web apps will need a way for users to sign up.
Anyway, adding a registrations controller is pretty straightforward:
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: "Signed up successfully"
else
render :new, status: :unprocessable_entity
end
end
private
def user_params
params.expect(user: [:email_address, :password, :password_confirmation])
end
end
As an aside,
the user_params
method is using the new params.expect
API
introduced in Rails 8.
Previous versions of Rails
used the params.require(...).permit(...)
syntax.
The registrations/new
view looks like this:
<%= form_with(model: @user, url: signup_path) do |form| %>
<div>
<%= form.label :email_address %>
<%= form.email_field :email_address, value: @user.email_address,
required: true, autofocus: true, autocomplete: "email" %>
</div>
<div>
<%= form.label :password %>
<%= form.password_field :password,
required: true, autocomplete: "new-password" %>
</div>
<div>
<%= form.label :password_confirmation %>
<%= form.password_field :password_confirmation,
required: true, autocomplete: "new-password" %>
</div>
<div>
<%= form.submit "Sign up" %>
</div>
<% end %>
Don’t forget to hook this up to config/routes.rb
:
get "signup", to: "registrations#new"
post "signup", to: "registrations#create"
Final thoughts
For a while now,
Rails has had many of the building blocks
of an authentication system available by default
in the form of methods like has_secure_password
.
It’s easy to roll your own authentication with this,
it is nice to have a starting point generated for you.
I wish this generator were a bit more comprehensive than it is right now. The lack of a registrations controller was surprising. And adding a few tests, much like the ones generated with controller scaffolds would have helped people understand how to write good tests for this.
That said, this is a great starting point, and it’s nice to be able to remove separate dependencies for authentication.