Fix sign_in_after_change_password? when :registerable is not included#5828
Conversation
7af3243 to
ec289ce
Compare
The `sign_in_after_change_password?` method in `RegistrationsController` calls `resource_class.sign_in_after_change_password`, which is only defined by the `:registerable` module via `Devise::Models.config`. When a model does not include `:registerable` (e.g. to prevent self-registration while still using the registrations controller for password changes), this raises a NoMethodError. Fall back to the global `Devise.sign_in_after_change_password` config when the resource class doesn't respond to the method. Add a controller test verifying the fallback uses the global Devise config when the model does not include `:registerable`. Fixes heartcombo#5827
ec289ce to
2c89089
Compare
|
Thanks for the very detailed issue and PR! The fix here is actually to include the You can't use passwords controller without recoverable, confirmations controller without confirmable, and so on. Same for registrations. If you don't include the module in the model, you'll likely run into trouble with any of the other Also note: if you don't include the module, the routes aren't generated by Devise, so # routes.rb
devise_for :users, skip: :registrations
devise_scope :user do
get 'profile/password', to: 'users/registrations#edit'
patch :users, to: 'users/registrations#update'
end
# User model — :registerable intentionally excluded
devise :database_authenticatable, :confirmable, :recoverable, :rememberable, :validatableIs essentially mapping a couple routes to your own registerable is a very thin module, adds almost nothing to the model, and you're already skipping the routes so I can't think of any issue that could happen including it... but you can also override |
|
Thanks for the thorough explanation, @carlosantoniodasilva! That clarifies the intended design. The insight about Adding Thanks again for taking the time to explain the design rationale! |
|
I had the same issue. Are you planning on updating the README to explain the breaking consequences of not having |
|
@jtFrancisco sorry you ran into this as well... honestly I'm not sure where in the README I'd try to explain that. Devise modules are defined in the model, the routes/controllers options are mainly for customizations. It happened to work before because get "up" => "rails/health#show", as: :rails_health_checki.e. pointing to a controller that's exposed by the framework/library. Except Rails does not have the "model" module contract, Devise does (or it was supposed to). I guess we could maybe expand the CHANGELOG to mention that people might run into this issue if they are pointing routes to the Devise registrations controller without the module defined? (and that it should be explicitly defined in that case) Would that work/help? |
Check the related issue for more info and explanation: #5828 (comment) [ci skip]
|
Added a note: 3fd0610 |
Summary
NoMethodErrorinRegistrationsController#sign_in_after_change_password?when the resource model does not include the:registerablemoduleDevise.sign_in_after_change_passwordconfig whenresource_classdoesn't respond to the methodContext
PR #5825 (released in 5.0.2) introduced per-model override for
sign_in_after_change_passwordvia the:registerablemodule. However, theRegistrationsControllercan be subclassed and used even when:registerableis not included — a common pattern to allow password updates while preventing self-registration.In this case,
resource_class.sign_in_after_change_passwordraises:The fix uses
respond_to?to gracefully fall back to the global Devise config when the model-level method isn't available.Why this use case should be supported
Devise's own architecture encourages this pattern. The
RegistrationsControlleris explicitly designed to be subclassed (Devise even provides a generator for custom controllers), and thedevise_forhelper supportsskip: :registrationswith customdevise_scoperoutes — making it straightforward to use the registrations controller for password management while disabling self-registration:This worked in Devise 5.0.0 and 5.0.1. The regression in 5.0.2 breaks any app using this documented routing pattern.
Test plan
resource_classwith a plain class (no:registerable) and verifiessign_in_after_change_password?falls back to global Devise configtrueandfalsevalues ofDevise.sign_in_after_change_passwordNoMethodErrorwhen the fix is revertedFixes #5827