How To Set Up Devise for Rails 7

Steve Condylios
3 min readApr 24, 2022

--

It’s easy to forget a step or two when setting up devise, so here’s a quick ‘how to’ setup devise. It’s not absolutely minimal, but it’s how I set things up for a new project. Here goes:

First, create a user model, resource or scaffold. For example:

rails g resource user first_name last_name role last_seen_at:datetime time_zone

Note: we don’t add an email column to the users table just yet (devise will do that later).

Add devise and letter_opener gems by running:

bundle add devise
bundle add letter_opener

Run the devise install generator:

rails g devise:install

Add these to the bottom of config/development.rb

# For devise
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
# For letter_opener
config.action_mailer.delivery_method = :letter_opener
config.action_mailer.perform_deliveries = true

In the same file, it can be useful to ensure errors are logged in development, so change that existing setting from false to true:

config.action_mailer.raise_delivery_errors = true

Add devise to the user model

rails g devise user

Go into the newly created migration file (something like db/migrate/20230720142500_add_devise_to_users.rb) and uncomment the confirmable and trackable sections if you want those (they’re usually nice to have).

Go into app/models/user.rb and add the relevant symbols e.g. :confirmable and :trackable, like so:

class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable, :trackable
end

If you prefer users to be ‘remembered’ for longer than 2 weeks, change this setting in config/initializers/devise.rb (users may expect to stay signed in on devices they’ve signed in on, so setting to something large, e.g. 6.months or 1.year or similar may be desirable).

config.remember_for = 2.years

In the same file, change the default email to one from your application:

config.mailer_sender = 'no-reply@example.com'

Generate devise views (so you can edit them to your taste):

rails g devise:views

Migrate:

rails db:migrate

Add this above <%= yield %> in app/views/layouts/application.html.erb

<% if notice %>
<p class="alert alert-success"><%= notice %></p>
<% end %>
<% if alert %>
<p class="alert alert-danger"><%= alert %></p>
<% end %>

Note: if you had <%= notice %> in other views, it can be removed since it’s now in application.html.erb and so it applies to all views automatically (if you generated other views with scaffolds, you may see notices twice until you remove the extraneous ones).

Try it out

Head to http://localhost:3000/users/sign_up and make a new user, you should receive an email which opens in the browser (via letter_opener). Great, it’s working!

Denying access!

Let’s test authorisation by creating two pages: a landing page which is accessible to anyone, and a dashboard which is accessible only after an account is created and the user has logged in:

rails g controller static_pages landing_page dashboard

Start the server and note that both pages are accessible:

http://localhost:3000/static_pages/landing_page

http://localhost:3000/static_pages/dashboard

In the application controller, add this

before_action :authenticate_user!

Now the two pages will redirect to a login screen. To make the landing_page accessible without login, go into the static_pages controller and add this:

skip_before_action :authenticate_user!, only: [:landing_page]

Set a root url in routes.rb by replacing the landing_page route with this:

root 'static_pages#landing_page'

Lastly, add sign in and sign out links just after <body> in app/views/application.html.erb like so:

<% if user_signed_in? %>
Logged in as <strong><%= current_user.email %></strong>.
<%= link_to 'Edit profile', edit_user_registration_path %> |
<%= link_to "Logout", destroy_user_session_path, data: { turbo_method: :delete } %>
<% else %>
<%= link_to "Sign up", new_user_registration_path %> |
<%= link_to "Login", new_user_session_path %>
<% end %>

Create a new user, login, log out, have a play around.

Devise with Rails 7 and Turbo

When Rails 7 first launched, there were some incompatibilities between Rails 7, devise and Turbo. As of July 2023, there aren’t any problems. However, when first written, this guide showed how to handle them. If you’re looking for those old instructions, you can find them via WayBack Machine, as well as some useful resources here, here, and here.

Depending on how you manage javascript in your rails app, you may have to open another terminal window and start your server with ./bin/dev ensure javascript/turbo works.

One last thing, when using Rails 7 and Turbo, the logout button will require an additional argument from pre-Turbo versions:

<%= link_to "Logout", destroy_user_session_path, data: { turbo_method: :delete } %>

This is a common gotcha, so is worth mentioning to save you some time.

Further resources

  • Devise ‘Getting Started’ docs (here).
  • Rails Girls guide to devise (here).
  • Devise youtube tutorial (here).
  • Letter_opener docs (here).

--

--