Nithin Bekal About

Rails 8 authentication generator

22 Oct 2024

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.

Hi, I’m Nithin! This is my blog about programming. Ruby is my programming language of choice and the topic of most of my articles here, but I occasionally also write about Elixir, and sometimes about the books I read. You can use the atom feed if you wish to subscribe to this blog or follow me on Mastodon.