devise: Devise 4.8.1 + Rails 7.0.0 | Undefined method 'user_url'

Pre-check

  • Created a new rails-app with rails new app-name -a propshaft -j importmap -c tailwind
  • Added gem 'devise', '~> 4.8', '>= 4.8.1' to Gemfile and followed installation instructions

Environment

  • Ruby 3.0.3p157
  • Rails 7.0.0
  • Devise 4.8.1

Current behavior

  • Receiving NoMethodError (undefined method 'user_url' for #<Devise::RegistrationsController:0x0000000000d638>) upon successful user registration.
  • User gets persisted in database

Demo app to reproduce the error is available here

Expected behavior

  • Redirect to root_path without error

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 38
  • Comments: 25 (5 by maintainers)

Commits related to this issue

Most upvoted comments

Ran into this at the same time as you 😆

Quick fix, tl;dr

Add :turbo_stream as a navigational format. This line goes in config/initializers/devise.rb. I haven’t tested this extensively, but I think it should be fine.

config.navigational_formats = ['*/*', :html, :turbo_stream]

More detail

A few lines from a trace starting with the offending line in registrations controller: https://github.com/heartcombo/devise/blob/025b1c873491908b346e4d394f54481ec18fb02c/app/controllers/devise/registrations_controller.rb#L25

https://github.com/heartcombo/devise/blob/025b1c873491908b346e4d394f54481ec18fb02c/app/controllers/devise/registrations_controller.rb#L111-L113

https://github.com/heartcombo/devise/blob/025b1c873491908b346e4d394f54481ec18fb02c/lib/devise/controllers/helpers.rb#L264-L266

https://github.com/heartcombo/devise/blob/025b1c873491908b346e4d394f54481ec18fb02c/lib/devise.rb#L218-L220

Basically, requests in Rails 7 may come with format :turbo_stream rather than simply :html. This causes is_navigational_format? to be falsey and thus after_sign_up_path_for returns nil. With location: nil for responds_with, the redirect path resolves from the resource - in our case the user.

I’m happy to open a PR for this, though someone with more familiarity with turbo should sanity check it.

For now you have to disable turbo on devise links, i.e. data: { turbo: false }. I should have some time to look into more official turbo integration soon.

@erik-brueggemann my understanding is that method: :delete won’t work with a link_to when not using UJS (which is not a dependency by default in Rails 7). So the sign_out method is going to be called as a GET, which is the fallback, resulting in an error.

You can try changing link_to to button_to (which I don’t like), or add data-turbo-method=“delete”, like so:

<%= link_to "Log out", destroy_user_session_path,
data: {
 "turbo-method": :delete
}, class: "link-secondary smooth" %>

A problem I am trying to find a solution for with this approach is that upon singing out it won’t redirect to the root, but to the page the user was on before signing out, which, of course, most probably requires authorisation, so it is not the best experience.

The approach above works without the @nickrivadeneira fix (however, you might be experiencing a different issue). I’m not sure what is it changes exactly in the behaviour. I was hoping it will be a fix for my issue, but it isn’t… any help will be appreciated.

Hi @nortonandreev, the problem you’re describing is yet another one that resulted from the Rails 7 / turbo-rails aftermath as far as I can tell.

This particular issue here wasn’t meant to address the log out mechanism, but rather user registration only. For that, the fix suggested by @nickrivadeneira makes sense to me - nevertheless you’ve got a point! It’s probably a good idea to open a separate issue to find a permanent solution for that?

But since we’ve already brought it up, instead of changing from link_to to button_to and instead of adding the :delete data-tag, you might try editing your config/devise.rb to sign out via GET instead of DELETE

  # The default HTTP method used to sign out a resource. Default is :delete.
  config.sign_out_via = :get # <= change this from :delete to :get and remove the `method:` in your `link_to` helper

Happy to open up another issue if ya’ll think it’s two separate topics.

The changelog says that “Please note that Turbo integration is not fully supported by Devise yet.”. It would be nice if someone familiar with the matter updates us on what does this mean exactly and if there is some ETA of when full support will be added. I don’t really want to waste hours implementing hacky solutions which might make my app vulnerable. On the other hand, I don’t want to be waiting forever, if devise won’t be fully updated to support Rails 7.

Hi @nortonandreev, the problem you’re describing is yet another one that resulted from the Rails 7 / turbo-rails aftermath as far as I can tell.

This particular issue here wasn’t meant to address the log out mechanism, but rather user registration only. For that, the fix suggested by @nickrivadeneira makes sense to me - nevertheless you’ve got a point! It’s probably a good idea to open a separate issue to find a permanent solution for that?

But since we’ve already brought it up, instead of changing from link_to to button_to and instead of adding the :delete data-tag, you might try editing your config/devise.rb to sign out via GET instead of DELETE

  # The default HTTP method used to sign out a resource. Default is :delete.
  config.sign_out_via = :get # <= change this from :delete to :get and remove the `method:` in your `link_to` helper

Happy to open up another issue if ya’ll think it’s two separate topics.

The main branch should contain all that’s necessary for fully working with Turbo now, which should fix this. A new version will be released soon, but feel free to test it out from the main branch in the meantime, and report back on any issues. Thanks.

But since we’ve already brought it up, instead of changing from link_to to button_to and instead of adding the :delete data-tag, you might try editing your config/devise.rb to sign out via GET instead of DELETE

just curious - wouldn’t that open one up to csrf attacks?

As noted by @nortonandreev this worked for me in an app with Rails 7 and importmap

<%= link_to "Log out", destroy_user_session_path,
data: {
 "turbo-method": :delete
} %>

Ran into this at the same time as you laughing

Quick fix, tl;dr

Add :turbo_stream as a navigational format. This line goes in config/initializers/devise.rb. I haven’t tested this extensively, but I think it should be fine.

config.navigational_formats = ['*/*', :html, :turbo_stream]

More detail

A few lines from a trace starting with the offending line in registrations controller:

https://github.com/heartcombo/devise/blob/025b1c873491908b346e4d394f54481ec18fb02c/app/controllers/devise/registrations_controller.rb#L25

https://github.com/heartcombo/devise/blob/025b1c873491908b346e4d394f54481ec18fb02c/app/controllers/devise/registrations_controller.rb#L111-L113

https://github.com/heartcombo/devise/blob/025b1c873491908b346e4d394f54481ec18fb02c/lib/devise/controllers/helpers.rb#L264-L266

https://github.com/heartcombo/devise/blob/025b1c873491908b346e4d394f54481ec18fb02c/lib/devise.rb#L218-L220

Basically, requests in Rails 7 may come with format :turbo_stream rather than simply :html. This causes is_navigational_format? to be falsey and thus after_sign_up_path_for returns nil. With location: nil for responds_with, the redirect path resolves from the resource - in our case the user.

I’m happy to open a PR for this, though someone with more familiarity with turbo should sanity check it.

This solution worked for me too!

Hi everyone, This message is from Dec, 2022, I am currently making a rails app in which I do use devise, but certainly all of it works fine from registering a new user session to destroying the same. I am just a beginner at Ruby on Rails but this app needed nothing more.

This is what I did

<%= link_to "Log in", new_user_session_path %>

<%= link_to "Sign Up", new_user_registration_path %>

<%= link_to "Sign Out", destroy_user_session_path, 'data-turbo-method': :delete %>

I am attaching the repository You can check the application code to verify and let’s close the issue finally.

It works for me <%= button_to 'Logout', destroy_user_session_path, method: :delete, form: {turbolink: false} %>

I added , data: { turbo: false } to all link_to and form_for helpers on the Devise views.