From f0992e4a96352dbb2dfec731f46a51f99a013b29 Mon Sep 17 00:00:00 2001 From: Michael Borohovski Date: Fri, 13 Feb 2015 02:14:02 -0800 Subject: [PATCH] Added an option to not automatically sign in a user after a password reset. This is useful for cases where additional strategies might be needed (such as two-factor authentication, e.g.), or generally if it is considered a security risk to automatically log in a user after a password is reset. --- app/controllers/devise/passwords_controller.rb | 13 +++++++++---- lib/devise.rb | 6 +++++- lib/devise/models/recoverable.rb | 4 +++- lib/generators/templates/devise.rb | 4 ++++ test/integration/recoverable_test.rb | 13 +++++++++++++ test/rails_app/config/initializers/devise.rb | 4 ++++ 6 files changed, 38 insertions(+), 6 deletions(-) diff --git a/app/controllers/devise/passwords_controller.rb b/app/controllers/devise/passwords_controller.rb index c97d22da..3175ac57 100644 --- a/app/controllers/devise/passwords_controller.rb +++ b/app/controllers/devise/passwords_controller.rb @@ -34,10 +34,15 @@ class Devise::PasswordsController < DeviseController if resource.errors.empty? resource.unlock_access! if unlockable?(resource) - flash_message = resource.active_for_authentication? ? :updated : :updated_not_active - set_flash_message(:notice, flash_message) if is_flashing_format? - sign_in(resource_name, resource) - respond_with resource, location: after_resetting_password_path_for(resource) + if Devise.sign_in_after_reset_password + flash_message = resource.active_for_authentication? ? :updated : :updated_not_active + set_flash_message(:notice, flash_message) if is_flashing_format? + sign_in(resource_name, resource) + respond_with resource, location: after_resetting_password_path_for(resource) + else + set_flash_message(:notice, :updated_not_active) if is_flashing_format? + respond_with resource, location: new_session_path(resource_name) + end else respond_with resource end diff --git a/lib/devise.rb b/lib/devise.rb index 3be7f325..89b8b03a 100644 --- a/lib/devise.rb +++ b/lib/devise.rb @@ -204,6 +204,10 @@ module Devise mattr_accessor :reset_password_within @@reset_password_within = 6.hours + # When set to false, resetting a password does not automatically sign in a user + mattr_accessor :sign_in_after_reset_password + @@sign_in_after_reset_password = true + # The default scope which is used by warden. mattr_accessor :default_scope @@default_scope = nil @@ -362,7 +366,7 @@ module Devise # def self.add_module(module_name, options = {}) options.assert_valid_keys(:strategy, :model, :controller, :route, :no_input, :insert_at) - + ALL.insert (options[:insert_at] || -1), module_name if strategy = options[:strategy] diff --git a/lib/devise/models/recoverable.rb b/lib/devise/models/recoverable.rb index f5acfce2..3dce0d7c 100644 --- a/lib/devise/models/recoverable.rb +++ b/lib/devise/models/recoverable.rb @@ -8,6 +8,8 @@ module Devise # Recoverable adds the following options to devise_for: # # * +reset_password_keys+: the keys you want to use when recovering the password for an account + # * +reset_password_within+: the time period within which the password must be reset or the token expires. + # * +sign_in_after_reset_password+: whether or not to sign in the user automatically after a password reset. # # == Examples # @@ -150,7 +152,7 @@ module Devise recoverable end - Devise::Models.config(self, :reset_password_keys, :reset_password_within) + Devise::Models.config(self, :reset_password_keys, :reset_password_within, :sign_in_after_reset_password) end end end diff --git a/lib/generators/templates/devise.rb b/lib/generators/templates/devise.rb index bb448910..5891bc87 100644 --- a/lib/generators/templates/devise.rb +++ b/lib/generators/templates/devise.rb @@ -197,6 +197,10 @@ Devise.setup do |config| # change their passwords. config.reset_password_within = 6.hours + # When set to false, does not sign a user in automatically after their password is + # reset. Defaults to true, so a user is signed in automatically after a reset. + # config.sign_in_after_reset_password = true + # ==> Configuration for :encryptable # Allow you to use another encryption algorithm besides bcrypt (default). You can use # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, diff --git a/test/integration/recoverable_test.rb b/test/integration/recoverable_test.rb index 004748ef..d13caa8f 100644 --- a/test/integration/recoverable_test.rb +++ b/test/integration/recoverable_test.rb @@ -197,6 +197,19 @@ class PasswordTest < ActionDispatch::IntegrationTest assert warden.authenticated?(:user) end + test 'does not sign in user automatically after changing its password if config.sign_in_after_reset_password is false' do + swap Devise, sign_in_after_reset_password: false do + create_user + request_forgot_password + reset_password + + assert_contain 'Your password has been changed successfully.' + assert_not_contain 'You are now signed in.' + assert_equal new_user_session_path, @request.path + assert !warden.authenticated?(:user) + end + end + test 'does not sign in user automatically after changing its password if it\'s locked and unlock strategy is :none or :time' do [:none, :time].each do |strategy| swap Devise, unlock_strategy: strategy do diff --git a/test/rails_app/config/initializers/devise.rb b/test/rails_app/config/initializers/devise.rb index ef9a556b..0f5c3fd0 100644 --- a/test/rails_app/config/initializers/devise.rb +++ b/test/rails_app/config/initializers/devise.rb @@ -131,6 +131,10 @@ Devise.setup do |config| # change their passwords. config.reset_password_within = 2.hours + # When set to false, does not sign a user in automatically after their password is + # reset. Defaults to true, so a user is signed in automatically after a reset. + # config.sign_in_after_reset_password = true + # Setup a pepper to generate the encrypted password. config.pepper = "d142367154e5beacca404b1a6a4f8bc52c6fdcfa3ccc3cf8eb49f3458a688ee6ac3b9fae488432a3bfca863b8a90008368a9f3a3dfbe5a962e64b6ab8f3a3a1a"