sorcery 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sorcery might be problematic. Click here for more details.

Files changed (83) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +20 -0
  3. data/.rubocop.yml +52 -2
  4. data/.rubocop_todo.yml +1 -429
  5. data/.travis.yml +11 -21
  6. data/CHANGELOG.md +16 -0
  7. data/Gemfile +2 -2
  8. data/{LICENSE.txt → LICENSE.md} +1 -1
  9. data/README.md +7 -1
  10. data/gemfiles/{active_record-rails40.gemfile → active_record_rails_40.gemfile} +1 -2
  11. data/gemfiles/{active_record-rails41.gemfile → active_record_rails_41.gemfile} +1 -2
  12. data/gemfiles/{active_record-rails42.gemfile → active_record_rails_42.gemfile} +1 -2
  13. data/lib/generators/sorcery/USAGE +1 -1
  14. data/lib/generators/sorcery/install_generator.rb +21 -21
  15. data/lib/generators/sorcery/templates/initializer.rb +19 -1
  16. data/lib/sorcery/adapters/active_record_adapter.rb +1 -1
  17. data/lib/sorcery/adapters/mongoid_adapter.rb +23 -11
  18. data/lib/sorcery/controller.rb +22 -16
  19. data/lib/sorcery/controller/config.rb +2 -0
  20. data/lib/sorcery/controller/submodules/activity_logging.rb +4 -0
  21. data/lib/sorcery/controller/submodules/external.rb +37 -33
  22. data/lib/sorcery/controller/submodules/http_basic_auth.rb +1 -0
  23. data/lib/sorcery/controller/submodules/remember_me.rb +1 -7
  24. data/lib/sorcery/controller/submodules/session_timeout.rb +25 -4
  25. data/lib/sorcery/crypto_providers/aes256.rb +1 -0
  26. data/lib/sorcery/crypto_providers/bcrypt.rb +2 -1
  27. data/lib/sorcery/engine.rb +10 -3
  28. data/lib/sorcery/model.rb +9 -6
  29. data/lib/sorcery/model/config.rb +3 -3
  30. data/lib/sorcery/model/submodules/brute_force_protection.rb +6 -7
  31. data/lib/sorcery/model/submodules/external.rb +4 -3
  32. data/lib/sorcery/model/submodules/magic_login.rb +29 -36
  33. data/lib/sorcery/model/submodules/reset_password.rb +5 -4
  34. data/lib/sorcery/model/submodules/user_activation.rb +1 -1
  35. data/lib/sorcery/protocols/oauth.rb +1 -0
  36. data/lib/sorcery/providers/auth0.rb +46 -0
  37. data/lib/sorcery/providers/heroku.rb +1 -0
  38. data/lib/sorcery/providers/instagram.rb +73 -0
  39. data/lib/sorcery/providers/linkedin.rb +1 -1
  40. data/lib/sorcery/providers/vk.rb +1 -1
  41. data/lib/sorcery/providers/wechat.rb +8 -6
  42. data/lib/sorcery/test_helpers/internal.rb +5 -4
  43. data/lib/sorcery/test_helpers/internal/rails.rb +11 -11
  44. data/lib/sorcery/version.rb +1 -1
  45. data/sorcery.gemspec +25 -9
  46. data/spec/active_record/user_activation_spec.rb +2 -2
  47. data/spec/active_record/user_activity_logging_spec.rb +2 -2
  48. data/spec/active_record/user_brute_force_protection_spec.rb +2 -2
  49. data/spec/active_record/user_magic_login_spec.rb +4 -4
  50. data/spec/active_record/user_oauth_spec.rb +2 -2
  51. data/spec/active_record/user_remember_me_spec.rb +2 -2
  52. data/spec/active_record/user_reset_password_spec.rb +2 -2
  53. data/spec/active_record/user_spec.rb +0 -10
  54. data/spec/controllers/controller_http_basic_auth_spec.rb +1 -1
  55. data/spec/controllers/controller_oauth2_spec.rb +195 -123
  56. data/spec/controllers/controller_oauth_spec.rb +7 -7
  57. data/spec/controllers/controller_remember_me_spec.rb +11 -6
  58. data/spec/controllers/controller_session_timeout_spec.rb +90 -3
  59. data/spec/controllers/controller_spec.rb +2 -2
  60. data/spec/orm/active_record.rb +2 -2
  61. data/spec/providers/vk_spec.rb +13 -12
  62. data/spec/rails_app/app/controllers/sorcery_controller.rb +83 -32
  63. data/spec/rails_app/app/mailers/sorcery_mailer.rb +1 -1
  64. data/spec/rails_app/config/application.rb +8 -3
  65. data/spec/rails_app/config/boot.rb +1 -1
  66. data/spec/rails_app/config/environment.rb +1 -1
  67. data/spec/rails_app/config/routes.rb +7 -0
  68. data/spec/rails_app/config/secrets.yml +4 -0
  69. data/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +2 -2
  70. data/spec/rails_app/db/migrate/invalidate_active_sessions/20180221093235_add_invalidate_active_sessions_before_to_users.rb +9 -0
  71. data/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb +3 -3
  72. data/spec/rails_app/db/schema.rb +7 -9
  73. data/spec/shared_examples/user_magic_login_shared_examples.rb +50 -50
  74. data/spec/shared_examples/user_oauth_shared_examples.rb +1 -1
  75. data/spec/shared_examples/user_remember_me_shared_examples.rb +1 -1
  76. data/spec/shared_examples/user_reset_password_shared_examples.rb +3 -3
  77. data/spec/shared_examples/user_shared_examples.rb +41 -43
  78. data/spec/sorcery_crypto_providers_spec.rb +1 -1
  79. data/spec/spec.opts +1 -1
  80. data/spec/spec_helper.rb +2 -2
  81. data/spec/support/migration_helper.rb +19 -0
  82. metadata +60 -38
  83. data/spec/rails_app/config/initializers/secret_token.rb +0 -7
@@ -18,6 +18,7 @@ module Sorcery
18
18
  attr_accessor :after_failed_login
19
19
  attr_accessor :before_logout
20
20
  attr_accessor :after_logout
21
+ attr_accessor :after_remember_me
21
22
 
22
23
  def init!
23
24
  @defaults = {
@@ -29,6 +30,7 @@ module Sorcery
29
30
  :@after_failed_login => [],
30
31
  :@before_logout => [],
31
32
  :@after_logout => [],
33
+ :@after_remember_me => [],
32
34
  :@save_return_to_url => true,
33
35
  :@cookie_domain => nil
34
36
  }
@@ -43,6 +43,7 @@ module Sorcery
43
43
  # This runs as a hook just after a successful login.
44
44
  def register_login_time_to_db(user, _credentials)
45
45
  return unless Config.register_login_time
46
+
46
47
  user.set_last_login_at(Time.now.in_time_zone)
47
48
  end
48
49
 
@@ -50,6 +51,7 @@ module Sorcery
50
51
  # This runs as a hook just before a logout.
51
52
  def register_logout_time_to_db
52
53
  return unless Config.register_logout_time
54
+
53
55
  current_user.set_last_logout_at(Time.now.in_time_zone)
54
56
  end
55
57
 
@@ -58,6 +60,7 @@ module Sorcery
58
60
  def register_last_activity_time_to_db
59
61
  return unless Config.register_last_activity_time
60
62
  return unless logged_in?
63
+
61
64
  current_user.set_last_activity_at(Time.now.in_time_zone)
62
65
  end
63
66
 
@@ -65,6 +68,7 @@ module Sorcery
65
68
  # This runs as a hook just after a successful login.
66
69
  def register_last_ip_address(_user, _credentials)
67
70
  return unless Config.register_last_ip_address
71
+
68
72
  current_user.set_last_ip_address(request.remote_ip)
69
73
  end
70
74
  end
@@ -23,6 +23,8 @@ module Sorcery
23
23
  require 'sorcery/providers/slack'
24
24
  require 'sorcery/providers/wechat'
25
25
  require 'sorcery/providers/microsoft'
26
+ require 'sorcery/providers/instagram'
27
+ require 'sorcery/providers/auth0'
26
28
 
27
29
  Config.module_eval do
28
30
  class << self
@@ -33,17 +35,17 @@ module Sorcery
33
35
  @external_providers = providers
34
36
 
35
37
  providers.each do |name|
36
- class_eval <<-E
38
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
37
39
  def self.#{name}
38
40
  @#{name} ||= Sorcery::Providers.const_get('#{name}'.to_s.capitalize).new
39
41
  end
40
- E
42
+ RUBY
41
43
  end
42
44
  end
43
45
 
44
46
  def merge_external_defaults!
45
47
  @defaults.merge!(:@external_providers => [],
46
- :@ca_file => File.join(File.expand_path(File.dirname(__FILE__)), '../../protocols/certs/ca-bundle.crt'))
48
+ :@ca_file => File.join(__dir__, '../../protocols/certs/ca-bundle.crt'))
47
49
  end
48
50
  end
49
51
  merge_external_defaults!
@@ -56,6 +58,7 @@ module Sorcery
56
58
  # save the singleton ProviderClient instance into @provider
57
59
  def sorcery_get_provider(provider_name)
58
60
  return unless Config.external_providers.include?(provider_name.to_sym)
61
+
59
62
  Config.send(provider_name.to_sym)
60
63
  end
61
64
 
@@ -64,12 +67,11 @@ module Sorcery
64
67
  def sorcery_login_url(provider_name, args = {})
65
68
  @provider = sorcery_get_provider provider_name
66
69
  sorcery_fixup_callback_url @provider
67
- if @provider.respond_to?(:login_url) && @provider.has_callback?
68
- @provider.state = args[:state]
69
- return @provider.login_url(params, session)
70
- else
71
- return nil
72
- end
70
+
71
+ return nil unless @provider.respond_to?(:login_url) && @provider.has_callback?
72
+
73
+ @provider.state = args[:state]
74
+ @provider.login_url(params, session)
73
75
  end
74
76
 
75
77
  # get the user hash from a provider using information from the params and session.
@@ -88,6 +90,7 @@ module Sorcery
88
90
  # cache them in instance variables.
89
91
  @access_token ||= @provider.process_callback(params, session) # sends request to oauth agent to get the token
90
92
  @user_hash ||= @provider.get_user_hash(@access_token) # uses the token to send another request to the oauth agent requesting user info
93
+ nil
91
94
  end
92
95
 
93
96
  # for backwards compatibility
@@ -98,14 +101,15 @@ module Sorcery
98
101
  # this method should be somewhere else. It only does something once per application per provider.
99
102
  def sorcery_fixup_callback_url(provider)
100
103
  provider.original_callback_url ||= provider.callback_url
101
- if provider.original_callback_url.present? && provider.original_callback_url[0] == '/'
102
- uri = URI.parse(request.url.gsub(/\?.*$/, ''))
103
- uri.path = ''
104
- uri.query = nil
105
- uri.scheme = 'https' if request.env['HTTP_X_FORWARDED_PROTO'] == 'https'
106
- host = uri.to_s
107
- provider.callback_url = "#{host}#{@provider.original_callback_url}"
108
- end
104
+
105
+ return unless provider.original_callback_url.present? && provider.original_callback_url[0] == '/'
106
+
107
+ uri = URI.parse(request.url.gsub(/\?.*$/, ''))
108
+ uri.path = ''
109
+ uri.query = nil
110
+ uri.scheme = 'https' if request.env['HTTP_X_FORWARDED_PROTO'] == 'https'
111
+ host = uri.to_s
112
+ provider.callback_url = "#{host}#{@provider.original_callback_url}"
109
113
  end
110
114
 
111
115
  # sends user to authenticate at the provider's website.
@@ -118,26 +122,26 @@ module Sorcery
118
122
  def login_from(provider_name, should_remember = false)
119
123
  sorcery_fetch_user_hash provider_name
120
124
 
121
- if user = user_class.load_from_provider(provider_name, @user_hash[:uid].to_s)
122
- # we found the user.
123
- # clear the session
124
- return_to_url = session[:return_to_url]
125
- reset_sorcery_session
126
- session[:return_to_url] = return_to_url
125
+ return unless (user = user_class.load_from_provider(provider_name, @user_hash[:uid].to_s))
127
126
 
128
- # sign in the user
129
- auto_login(user, should_remember)
130
- after_login!(user)
127
+ # we found the user.
128
+ # clear the session
129
+ return_to_url = session[:return_to_url]
130
+ reset_sorcery_session
131
+ session[:return_to_url] = return_to_url
131
132
 
132
- # return the user
133
- user
134
- end
133
+ # sign in the user
134
+ auto_login(user, should_remember)
135
+ after_login!(user)
136
+
137
+ # return the user
138
+ user
135
139
  end
136
140
 
137
141
  # If user is logged, he can add all available providers into his account
138
142
  def add_provider_to_user(provider_name)
139
143
  sorcery_fetch_user_hash provider_name
140
- config = user_class.sorcery_config
144
+ # config = user_class.sorcery_config # TODO: Unused, remove?
141
145
 
142
146
  current_user.add_provider_to_user(provider_name.to_s, @user_hash[:uid].to_s)
143
147
  end
@@ -181,7 +185,7 @@ module Sorcery
181
185
  #
182
186
  def create_from(provider_name, &block)
183
187
  sorcery_fetch_user_hash provider_name
184
- config = user_class.sorcery_config
188
+ # config = user_class.sorcery_config # TODO: Unused, remove?
185
189
 
186
190
  attrs = user_attrs(@provider.user_info_mapping, @user_hash)
187
191
  @user = user_class.create_from_provider(provider_name, @user_hash[:uid], attrs, &block)
@@ -190,7 +194,7 @@ module Sorcery
190
194
  # follows the same patterns as create_from, but builds the user instead of creating
191
195
  def build_from(provider_name, &block)
192
196
  sorcery_fetch_user_hash provider_name
193
- config = user_class.sorcery_config
197
+ # config = user_class.sorcery_config # TODO: Unused, remove?
194
198
 
195
199
  attrs = user_attrs(@provider.user_info_mapping, @user_hash)
196
200
  @user = user_class.build_from_provider(attrs, &block)
@@ -202,7 +206,7 @@ module Sorcery
202
206
  if (varr = v.split('/')).size > 1
203
207
  attribute_value = begin
204
208
  varr.inject(user_hash[:user_info]) { |hash, value| hash[value] }
205
- rescue
209
+ rescue StandardError
206
210
  nil
207
211
  end
208
212
  attribute_value.nil? ? attrs : attrs.merge!(k => attribute_value)
@@ -57,6 +57,7 @@ module Sorcery
57
57
  while current_controller != ActionController::Base
58
58
  result = Config.controller_to_realm_map[current_controller.controller_name]
59
59
  return result if result
60
+
60
61
  current_controller = current_controller.superclass
61
62
  end
62
63
  nil
@@ -18,7 +18,6 @@ module Sorcery
18
18
  merge_remember_me_defaults!
19
19
  end
20
20
  Config.login_sources << :login_from_cookie
21
- Config.after_login << :remember_me_if_asked_to
22
21
  Config.before_logout << :forget_me!
23
22
  end
24
23
 
@@ -51,12 +50,6 @@ module Sorcery
51
50
 
52
51
  protected
53
52
 
54
- # calls remember_me! if a third credential was passed to the login method.
55
- # Runs as a hook after login.
56
- def remember_me_if_asked_to(_user, credentials)
57
- remember_me! if credentials.size == 3 && credentials[2] && credentials[2] != '0'
58
- end
59
-
60
53
  # Checks the cookie for a remember me token, tried to find a user with that token
61
54
  # and logs the user in if found.
62
55
  # Runs as a login source. See 'current_user' method for how it is used.
@@ -65,6 +58,7 @@ module Sorcery
65
58
  if user && user.has_remember_me_token?
66
59
  set_remember_me_cookie!(user)
67
60
  session[:user_id] = user.id.to_s
61
+ after_remember_me!(user)
68
62
  @current_user = user
69
63
  else
70
64
  @current_user = false
@@ -12,24 +12,36 @@ module Sorcery
12
12
  attr_accessor :session_timeout
13
13
  # use the last action as the beginning of session timeout.
14
14
  attr_accessor :session_timeout_from_last_action
15
+ # allow users to invalidate active sessions
16
+ attr_accessor :session_timeout_invalidate_active_sessions_enabled
15
17
 
16
18
  def merge_session_timeout_defaults!
17
- @defaults.merge!(:@session_timeout => 3600, # 1.hour
18
- :@session_timeout_from_last_action => false)
19
+ @defaults.merge!(:@session_timeout => 3600, # 1.hour
20
+ :@session_timeout_from_last_action => false,
21
+ :@session_timeout_invalidate_active_sessions_enabled => false)
19
22
  end
20
23
  end
21
24
  merge_session_timeout_defaults!
22
25
  end
23
26
  Config.after_login << :register_login_time
27
+ Config.after_remember_me << :register_login_time
24
28
  base.prepend_before_action :validate_session
25
29
  end
26
30
 
27
31
  module InstanceMethods
32
+ def invalidate_active_sessions!
33
+ return unless Config.session_timeout_invalidate_active_sessions_enabled
34
+ return unless current_user.present?
35
+
36
+ current_user.send(:invalidate_sessions_before=, Time.now.in_time_zone)
37
+ current_user.save
38
+ end
39
+
28
40
  protected
29
41
 
30
42
  # Registers last login to be used as the timeout starting point.
31
43
  # Runs as a hook after a successful login.
32
- def register_login_time(_user, _credentials)
44
+ def register_login_time(_user, _credentials = nil)
33
45
  session[:login_time] = session[:last_action_time] = Time.now.in_time_zone
34
46
  end
35
47
 
@@ -37,7 +49,7 @@ module Sorcery
37
49
  # To be used as a before_action, before require_login
38
50
  def validate_session
39
51
  session_to_use = Config.session_timeout_from_last_action ? session[:last_action_time] : session[:login_time]
40
- if session_to_use && sorcery_session_expired?(session_to_use.to_time)
52
+ if (session_to_use && sorcery_session_expired?(session_to_use.to_time)) || sorcery_session_invalidated?
41
53
  reset_sorcery_session
42
54
  remove_instance_variable :@current_user if defined? @current_user
43
55
  else
@@ -48,6 +60,15 @@ module Sorcery
48
60
  def sorcery_session_expired?(time)
49
61
  Time.now.in_time_zone - time > Config.session_timeout
50
62
  end
63
+
64
+ # Use login time if present, otherwise use last action time.
65
+ def sorcery_session_invalidated?
66
+ return false unless Config.session_timeout_invalidate_active_sessions_enabled
67
+ return false unless current_user.present? && current_user.try(:invalidate_sessions_before).present?
68
+
69
+ time = session[:login_time] || session[:last_action_time] || Time.now.in_time_zone
70
+ time < current_user.invalidate_sessions_before
71
+ end
51
72
  end
52
73
  end
53
74
  end
@@ -43,6 +43,7 @@ module Sorcery
43
43
 
44
44
  def aes
45
45
  raise ArgumentError, "#{name} expects a 32 bytes long key. Please use Sorcery::Model::Config.encryption_key to set it." if @key.nil? || @key == ''
46
+
46
47
  @aes ||= OpenSSL::Cipher.new('AES-256-ECB')
47
48
  end
48
49
  end
@@ -60,6 +60,7 @@ module Sorcery
60
60
  def matches?(hash, *tokens)
61
61
  hash = new_from_hash(hash)
62
62
  return false if hash.nil? || hash == {}
63
+
63
64
  hash == join_tokens(tokens)
64
65
  end
65
66
 
@@ -87,7 +88,7 @@ module Sorcery
87
88
  def new_from_hash(hash)
88
89
  ::BCrypt::Password.new(hash)
89
90
  rescue ::BCrypt::Errors::InvalidHash
90
- return nil
91
+ nil
91
92
  end
92
93
  end
93
94
  end
@@ -8,9 +8,16 @@ module Sorcery
8
8
  config.sorcery = ::Sorcery::Controller::Config
9
9
 
10
10
  initializer 'extend Controller with sorcery' do
11
- ActionController::Base.send(:include, Sorcery::Controller)
12
- ActionController::Base.helper_method :current_user
13
- ActionController::Base.helper_method :logged_in?
11
+ # TODO: Should this include a modified version of the helper methods?
12
+ if defined?(ActionController::API)
13
+ ActionController::API.send(:include, Sorcery::Controller)
14
+ end
15
+
16
+ if defined?(ActionController::Base)
17
+ ActionController::Base.send(:include, Sorcery::Controller)
18
+ ActionController::Base.helper_method :current_user
19
+ ActionController::Base.helper_method :logged_in?
20
+ end
14
21
  end
15
22
  end
16
23
  end
@@ -47,12 +47,15 @@ module Sorcery
47
47
  class_eval do
48
48
  @sorcery_config.submodules = ::Sorcery::Controller::Config.submodules
49
49
  @sorcery_config.submodules.each do |mod|
50
+ # TODO: Is there a cleaner way to handle missing submodules?
51
+ # rubocop:disable Lint/HandleExceptions
50
52
  begin
51
53
  include Submodules.const_get(mod.to_s.split('_').map(&:capitalize).join)
52
54
  rescue NameError
53
55
  # don't stop on a missing submodule. Needed because some submodules are only defined
54
56
  # in the controller side.
55
57
  end
58
+ # rubocop:enable Lint/HandleExceptions
56
59
  end
57
60
  end
58
61
  end
@@ -192,9 +195,9 @@ module Sorcery
192
195
  config = sorcery_config
193
196
  send(:"#{config.password_attribute_name}=", nil)
194
197
 
195
- if respond_to?(:"#{config.password_attribute_name}_confirmation=")
196
- send(:"#{config.password_attribute_name}_confirmation=", nil)
197
- end
198
+ return unless respond_to?(:"#{config.password_attribute_name}_confirmation=")
199
+
200
+ send(:"#{config.password_attribute_name}_confirmation=", nil)
198
201
  end
199
202
 
200
203
  # calls the requested email method on the configured mailer
@@ -202,9 +205,9 @@ module Sorcery
202
205
  def generic_send_email(method, mailer)
203
206
  config = sorcery_config
204
207
  mail = config.send(mailer).send(config.send(method), self)
205
- if defined?(ActionMailer) && config.send(mailer).is_a?(Class) && config.send(mailer) < ActionMailer::Base
206
- mail.send(config.email_delivery_method)
207
- end
208
+ return unless defined?(ActionMailer) && config.send(mailer).is_a?(Class) && config.send(mailer) < ActionMailer::Base
209
+
210
+ mail.send(config.email_delivery_method)
208
211
  end
209
212
  end
210
213
  end
@@ -4,8 +4,6 @@
4
4
  module Sorcery
5
5
  module Model
6
6
  class Config
7
- # change default username attribute, for example, to use :email as the login.
8
- attr_accessor :username_attribute_names
9
7
  # change *virtual* password attribute, the one which is used until an encrypted one is generated.
10
8
  attr_accessor :password_attribute_name
11
9
  # change default email attribute.
@@ -38,6 +36,8 @@ module Sorcery
38
36
  # Set token randomness
39
37
  attr_accessor :token_randomness
40
38
 
39
+ # change default username attribute, for example, to use :email as the login. See 'username_attribute_names=' below.
40
+ attr_reader :username_attribute_names
41
41
  # change default encryption_provider.
42
42
  attr_reader :encryption_provider
43
43
  # use an external encryption class.
@@ -96,7 +96,7 @@ module Sorcery
96
96
  when :bcrypt then CryptoProviders::BCrypt
97
97
  when :custom then @custom_encryption_provider
98
98
  else raise ArgumentError, "Encryption algorithm supplied, #{algo}, is invalid"
99
- end
99
+ end
100
100
  end
101
101
 
102
102
  private
@@ -14,7 +14,6 @@ module Sorcery
14
14
  :consecutive_login_retries_amount_limit, # how many failed logins allowed.
15
15
  :login_lock_time_period, # how long the user should be banned.
16
16
  # in seconds. 0 for permanent.
17
-
18
17
  :unlock_token_attribute_name, # Unlock token attribute name
19
18
  :unlock_token_email_method_name, # Mailer method name
20
19
  :unlock_token_mailer_disabled, # When true, dont send unlock token via email
@@ -70,9 +69,9 @@ module Sorcery
70
69
 
71
70
  sorcery_adapter.increment(config.failed_logins_count_attribute_name)
72
71
 
73
- if send(config.failed_logins_count_attribute_name) >= config.consecutive_login_retries_amount_limit
74
- login_lock!
75
- end
72
+ return unless send(config.failed_logins_count_attribute_name) >= config.consecutive_login_retries_amount_limit
73
+
74
+ login_lock!
76
75
  end
77
76
 
78
77
  # /!\
@@ -98,9 +97,9 @@ module Sorcery
98
97
  config.unlock_token_attribute_name => TemporaryToken.generate_random_token }
99
98
  sorcery_adapter.update_attributes(attributes)
100
99
 
101
- unless config.unlock_token_mailer_disabled || config.unlock_token_mailer.nil?
102
- send_unlock_token_email!
103
- end
100
+ return if config.unlock_token_mailer_disabled || config.unlock_token_mailer.nil?
101
+
102
+ send_unlock_token_email!
104
103
  end
105
104
 
106
105
  def login_unlocked?