Devise Auth Setup in Rails 7
Integrating Devise Auth with Rails 7 has some undocumented twists. Learn about them here or use the included template to bypass them
Rails 7 is here but — to my surprise — there are some twists to getting it working with Devise that I couldn’t find explained except by piecing together issues on the Devise GitHub and StackOverflow over the course of a few hours. To save you a bit of time, here’s how to do it.
Briefly, our target at the end of this article are to have a Rails 7 install with Devise’s default modules and auto-generated views working. The companion code to this article can be viewed in its entirety on GitHub. Moreover — if you are starting a fresh app — you can use the repo, which is configured as a template — to copy out as the starting point for your own app without needing to read anything further!
Generic Setup for Rails 7 and Devise
To start, I presume you already have a rails 7 application and whatever database you’re working with is set up. If you don’t, this takes no more than creating a rails app, as documented in step 3.2 of the Rails getting started guide. Assuming you have the dependencies installed (ruby, rails) all you need is to run the following in console to make a new app creatively named app_name
.
rails new app_name
cd app_name
To start our own journey, first add Devise as a dependency for your app. You can either add it your Gemfile manually, or pick up the latest version automatically by running the following in your console
bundle add devise
Your Gemfile should now include something like
# Gemfile
gem "devise", "~> 4.8"
Now install it, via the console.
bundle install
Then— just like on the Devise readme — we generate the Devise config.
rails g devise:install
Working Around Rails 7’s Turbo
Now, we need to do something a little special for Rails 7. Rails 7 includes Turbo as a cornerstone component. Turbo lets you run asynchronous page updates without writing any Javascript (which is nifty) but it does it by hijacking the normal flow of submitting forms and following links. Devise isn’t (yet) prepared for that and it won’t be able to display flash messages — which it relies heavily on — by default. We need to alter the code that Devise generates for us to deal with Turbo.
So, once you’ve run rails generate devise:install
we need to alter the Devise initializer config in several places beyond what the Devise README instructs us to do and add a controller as Devise’s parent controller. Credit where it’s due: these changes are from Go Rails video on the topic which also explains why these changes are necessary.
Back to Devise
As per normal Devise setup, we also need to tweak the action mailer config so that we can send password recovery emails in development.
# config/environments/development.rb, near the other action_mailer config. ~line 40 in an unaltered config file.config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
Also, as in normal Devise setup, we need to include the ability for our layout to show alert and notice flash notifications, since Devise uses them extensively to let us know the results of sign up, in, and out attempts.
# app/views/layouts/application.html.erb, above the <%= yield %><% if notice %>
<p class="alert alert-success"><%= notice %></p>
<% end %>
<% if alert %>
<p class="alert alert-danger"><%= alert %></p>
<% end %>
With the config all set up, we’re ready to generate our authenticatable model. We’ll use the standard name user
but — as is well documented in the devise README, you can name it whatever you want (though some of the method and file names mentioned later here will change). In console, run
rails g devise user
Make any modifications you need to the generated db/migrate/<timestamp>_devise_create_users.rb
file to add either your own columns or columns to support additional Devise modules. Here, I presume no modification is necessary. Then, run your migrations via the console
rake db:migrate
You’re now able to run your app and it should include pages like /users/sign_up
and /users/sign_in
. In theory Devise should work now but, unfortunately, the view links and redirects used by Devise won’t work in Rails 7. Let’s fix that.
If you navigate to /users/edit
in your browser (http://localhost:3000/users/edit
if you include the protocol and domain), and try to use the cancel account link it will cancel the account, but show an error when redirecting. The error will say something like undefined method `users_url’ for … RegistrationsController
. It’s telling us that, after deleting the user, Devise is trying to redirect us to the user#index
route which does not exist yet. Let’s route that path back to the sign in page.
# config/routes.rbRails.application.routes.draw do
devise_scope :user do
# Redirests signing out users back to sign-in
get "users", to: "devise/sessions#new"
enddevise_for :users
end
NOTE: I’ve tried to figure out how to change the url that Devise’s deletion redirects to, but so far none of the methods I’ve found online seem to work for Rails 7. If you figure it out, please let me know in the comments!
That should do it! Our out-of-the box Devise setup is now working with Rails 7. Once again, if you’d like to refer to any of the code for this setup, or use the template wholesale for a new app, the code is available on GitHub, and you may also use it as a template repo to kick off your own Rails 7 devise projects.
Final Notes: Turbo Links, Devise, and Deletes
If you later add a log-out link (or any other link) with a method: :delete
, the link will not work! Turbo will once again hijack the request and turn it into a GET
request instead, thus hitting the wrong controller endpoint. You can specify that the link should not use turbo (data: {turbo: false}
). That will work, but it will double send the delete request. I suspect it’s related to the precept that links shouldn’t change application state. DELETE
s do change application state. I suspect that’s why Turbo doesn’t support this behavior.
The easier and better alternative is to use a button to send the delete request, which works just fine.
<%= button_to(
"Log Out",
destroy_user_session_path,
method: :delete
) %>
Happy coding!
PS. Would you like to work with me selling pet drugs on the internet? Koala Health is hiring. Here’s a whole article about why you should work with us.