ユニファ開発者ブログ

ユニファ株式会社プロダクトデベロップメント本部メンバーによるブログです。

Rails 8 - Built-in authentication in depth look

By Harvey Ico, server-side engineer from Unifa

So, you want a user session feature in your application? Maybe you're already exploring gems like devise, sorcery, or others. Hold on! With the recent release of Rails 8, you can now achieve this without needing to install external gems.

Installation

To get started, the magic command is:

rails generate authentication

This command generates most of the necessary files for you.

Heads up! Before running this command, make sure you’ve created your database. If you don’t, Rails will skip creating migration files—something I learned on the way.

Here's an example of what gets generated:

➜  $ rails generate authentication
      invoke  erb
      create  app/views/passwords/new.html.erb
      create  app/views/passwords/edit.html.erb
      create  app/views/sessions/new.html.erb
      create  app/models/session.rb
      create  app/models/user.rb
      create  app/models/current.rb
      create  app/controllers/sessions_controller.rb
      create  app/controllers/concerns/authentication.rb
      create  app/controllers/passwords_controller.rb
      create  app/channels/application_cable/connection.rb
      create  app/mailers/passwords_mailer.rb
      create  app/views/passwords_mailer/reset.html.erb
      create  app/views/passwords_mailer/reset.text.erb
      create  test/mailers/previews/passwords_mailer_preview.rb
      insert  app/controllers/application_controller.rb
       route  resources :passwords, param: :token
       route  resource :session
        gsub  Gemfile
      bundle  install --quiet
/Users/harvey.ico/.rbenv/versions/3.3.6/bin/ruby: No such file or directory -- bin/bundle (LoadError)
    generate  migration CreateUsers email_address:string!:uniq password_digest:string! --force
       rails  generate migration CreateUsers email_address:string!:uniq password_digest:string! --force
      invoke  active_record
      create    db/migrate/20241127013611_create_users.rb
    generate  migration CreateSessions user:references ip_address:string user_agent:string --force
       rails  generate migration CreateSessions user:references ip_address:string user_agent:string --force
      invoke  active_record
      create    db/migrate/20241127013612_create_sessions.rb
Could not find gem 'bcrypt (~> 3.1.7)' in locally installed gems.
Run `bundle install --gemfile /Users/harvey.ico/projects/dummies/test_1/Gemfile` to install missing gems.

If you don’t already have the bcrypt gem installed, the generator will add it for you. In that case, you’ll need to run bundle install before proceeding.

Once that's done, run:

rails db:migrate

Example output:

➜  $ rails db:migrate
== 20241127013611 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0021s
-- add_index(:users, :email_address, {:unique=>true})
   -> 0.0004s
== 20241127013611 CreateUsers: migrated (0.0026s) =============================

== 20241127013612 CreateSessions: migrating ===================================
-- create_table(:sessions)
   -> 0.0071s
== 20241127013612 CreateSessions: migrated (0.0071s) ==========================

How to use

What’s next? How do we actually use this?

First, take a look at the files generated by the scaffold. You'll notice a file named:

app/controllers/concerns/authentication.rb

This concern should be included in any controller where authentication is required.

Typically, developers add it to the ApplicationController or a shared base controller. Conveniently, the generator automatically inserts the concern into ApplicationController for you. Depending on your use case, you may want to remove it from there.

class ApplicationController < ActionController::Base
  include Authentication
end

So, what’s in the Authentication concern? It includes several useful methods. Here are the key ones:

  • allow_unauthenticated_access: A class method to bypass authentication in certain scenarios.
  • authenticated?: Returns a Session object. However, I’d recommend renaming it to something more intuitive, like current_session, as its name might suggest it returns a boolean. You can access the associated User object from this method as well.
  • require_authentication: Use this in a before_action to enforce authentication.
  • after_authentication_url: Customize this method to define where users are redirected after logging in.
  • terminate_session: Use this method to log out a user.
  • start_new_session_for: Accepts a User object to start a new session. I suggest terminating the existing session first before using this.

Rails also sets up some basic session pages and routes for you.

For instance, the default login path is /sessions/new , which maps to the Rails helper new_session_path. The view is a basic skeleton so be sure to design it to your liking!

app/views/sessions/new.html.erb

You can view all authentication routes by running:

rails routes

Final thoughts

Until now, my go-to gem for authentication was devise. However, devise has become quite large and packed with features I rarely use. Removing unnecessary features and customizing it can be a hassle.

With the built-in Rails 8 authentication, I now have a lightweight, easy-to-setup alternative.

That said, note that this feature only provides the following:

  • User login page
  • Password reset page

What’s missing?

  • User sign up page

But don’t worry—that’s straightforward to implement. Creating a sign-up page usually involves inserting a User record with an email_address and password.


Unifa is actively recruiting, please check our website for details:

unifa-e.com