devise_token_auth 1.0.0 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +4 -2
  3. data/app/controllers/devise_token_auth/application_controller.rb +2 -3
  4. data/app/controllers/devise_token_auth/concerns/resource_finder.rb +11 -12
  5. data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +41 -57
  6. data/app/controllers/devise_token_auth/confirmations_controller.rb +63 -20
  7. data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +77 -29
  8. data/app/controllers/devise_token_auth/passwords_controller.rb +44 -30
  9. data/app/controllers/devise_token_auth/registrations_controller.rb +33 -40
  10. data/app/controllers/devise_token_auth/sessions_controller.rb +5 -5
  11. data/app/controllers/devise_token_auth/unlocks_controller.rb +4 -4
  12. data/app/models/devise_token_auth/concerns/active_record_support.rb +14 -0
  13. data/app/models/devise_token_auth/concerns/confirmable_support.rb +28 -0
  14. data/app/models/devise_token_auth/concerns/mongoid_support.rb +19 -0
  15. data/app/models/devise_token_auth/concerns/tokens_serialization.rb +31 -0
  16. data/app/models/devise_token_auth/concerns/user.rb +51 -70
  17. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +6 -3
  18. data/app/validators/{email_validator.rb → devise_token_auth_email_validator.rb} +2 -2
  19. data/config/locales/da-DK.yml +2 -0
  20. data/config/locales/de.yml +2 -0
  21. data/config/locales/en.yml +7 -0
  22. data/config/locales/es.yml +2 -0
  23. data/config/locales/fr.yml +2 -0
  24. data/config/locales/he.yml +52 -0
  25. data/config/locales/it.yml +2 -0
  26. data/config/locales/ja.yml +4 -2
  27. data/config/locales/ko.yml +51 -0
  28. data/config/locales/nl.yml +2 -0
  29. data/config/locales/pl.yml +6 -3
  30. data/config/locales/pt-BR.yml +2 -0
  31. data/config/locales/pt.yml +6 -3
  32. data/config/locales/ro.yml +2 -0
  33. data/config/locales/ru.yml +2 -0
  34. data/config/locales/sq.yml +2 -0
  35. data/config/locales/sv.yml +2 -0
  36. data/config/locales/uk.yml +2 -0
  37. data/config/locales/vi.yml +2 -0
  38. data/config/locales/zh-CN.yml +2 -0
  39. data/config/locales/zh-HK.yml +2 -0
  40. data/config/locales/zh-TW.yml +2 -0
  41. data/lib/devise_token_auth/blacklist.rb +2 -0
  42. data/lib/devise_token_auth/controllers/helpers.rb +5 -9
  43. data/lib/devise_token_auth/engine.rb +7 -1
  44. data/lib/devise_token_auth/rails/routes.rb +16 -11
  45. data/lib/devise_token_auth/token_factory.rb +126 -0
  46. data/lib/devise_token_auth/url.rb +3 -0
  47. data/lib/devise_token_auth/version.rb +1 -1
  48. data/lib/devise_token_auth.rb +6 -3
  49. data/lib/generators/devise_token_auth/USAGE +1 -1
  50. data/lib/generators/devise_token_auth/install_generator.rb +7 -91
  51. data/lib/generators/devise_token_auth/install_generator_helpers.rb +98 -0
  52. data/lib/generators/devise_token_auth/install_mongoid_generator.rb +46 -0
  53. data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +10 -0
  54. data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +1 -8
  55. data/lib/generators/devise_token_auth/templates/user.rb.erb +2 -2
  56. data/lib/generators/devise_token_auth/templates/user_mongoid.rb.erb +56 -0
  57. data/test/controllers/custom/custom_confirmations_controller_test.rb +1 -1
  58. data/test/controllers/demo_user_controller_test.rb +2 -2
  59. data/test/controllers/devise_token_auth/confirmations_controller_test.rb +83 -19
  60. data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +109 -42
  61. data/test/controllers/devise_token_auth/passwords_controller_test.rb +227 -102
  62. data/test/controllers/devise_token_auth/registrations_controller_test.rb +34 -7
  63. data/test/controllers/devise_token_auth/sessions_controller_test.rb +0 -38
  64. data/test/controllers/devise_token_auth/token_validations_controller_test.rb +2 -1
  65. data/test/dummy/app/active_record/confirmable_user.rb +11 -0
  66. data/test/dummy/app/{models → active_record}/scoped_user.rb +2 -2
  67. data/test/dummy/app/{models → active_record}/unconfirmable_user.rb +1 -2
  68. data/test/dummy/app/{models → active_record}/unregisterable_user.rb +3 -3
  69. data/test/dummy/app/active_record/user.rb +6 -0
  70. data/test/dummy/app/controllers/overrides/confirmations_controller.rb +3 -3
  71. data/test/dummy/app/controllers/overrides/passwords_controller.rb +3 -3
  72. data/test/dummy/app/controllers/overrides/registrations_controller.rb +1 -1
  73. data/test/dummy/app/controllers/overrides/sessions_controller.rb +2 -2
  74. data/test/dummy/app/models/{user.rb → concerns/favorite_color.rb} +7 -8
  75. data/test/dummy/app/mongoid/confirmable_user.rb +52 -0
  76. data/test/dummy/app/mongoid/lockable_user.rb +38 -0
  77. data/test/dummy/app/mongoid/mang.rb +46 -0
  78. data/test/dummy/app/mongoid/only_email_user.rb +33 -0
  79. data/test/dummy/app/mongoid/scoped_user.rb +50 -0
  80. data/test/dummy/app/mongoid/unconfirmable_user.rb +44 -0
  81. data/test/dummy/app/mongoid/unregisterable_user.rb +47 -0
  82. data/test/dummy/app/mongoid/user.rb +49 -0
  83. data/test/dummy/app/views/layouts/application.html.erb +0 -2
  84. data/test/dummy/config/application.rb +22 -1
  85. data/test/dummy/config/boot.rb +4 -0
  86. data/test/dummy/config/environments/development.rb +0 -10
  87. data/test/dummy/config/environments/production.rb +0 -16
  88. data/test/dummy/config/initializers/devise.rb +285 -0
  89. data/test/dummy/config/initializers/devise_token_auth.rb +35 -4
  90. data/test/dummy/config/initializers/figaro.rb +1 -1
  91. data/test/dummy/config/initializers/omniauth.rb +1 -0
  92. data/test/dummy/config/routes.rb +2 -0
  93. data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +0 -7
  94. data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +0 -7
  95. data/test/dummy/db/migrate/20141222035835_devise_token_auth_create_only_email_users.rb +0 -7
  96. data/test/dummy/db/migrate/20141222053502_devise_token_auth_create_unregisterable_users.rb +0 -7
  97. data/test/dummy/db/migrate/20150708104536_devise_token_auth_create_unconfirmable_users.rb +0 -7
  98. data/test/dummy/db/migrate/20160103235141_devise_token_auth_create_scoped_users.rb +0 -7
  99. data/test/dummy/db/migrate/20160629184441_devise_token_auth_create_lockable_users.rb +0 -7
  100. data/test/dummy/db/migrate/20190924101113_devise_token_auth_create_confirmable_users.rb +49 -0
  101. data/test/dummy/db/schema.rb +26 -28
  102. data/test/dummy/tmp/generators/app/models/azpire/v1/human_resource/user.rb +9 -0
  103. data/test/dummy/tmp/generators/config/initializers/devise_token_auth.rb +60 -0
  104. data/test/dummy/tmp/generators/db/migrate/20210126004321_devise_token_auth_create_azpire_v1_human_resource_users.rb +49 -0
  105. data/test/factories/users.rb +3 -2
  106. data/test/lib/devise_token_auth/blacklist_test.rb +11 -0
  107. data/test/lib/devise_token_auth/rails/custom_routes_test.rb +29 -0
  108. data/test/lib/devise_token_auth/rails/routes_test.rb +87 -0
  109. data/test/lib/devise_token_auth/token_factory_test.rb +191 -0
  110. data/test/lib/devise_token_auth/url_test.rb +2 -2
  111. data/test/lib/generators/devise_token_auth/install_generator_test.rb +51 -31
  112. data/test/lib/generators/devise_token_auth/install_generator_with_namespace_test.rb +51 -31
  113. data/test/models/concerns/mongoid_support_test.rb +31 -0
  114. data/test/models/concerns/tokens_serialization_test.rb +104 -0
  115. data/test/models/confirmable_user_test.rb +35 -0
  116. data/test/models/only_email_user_test.rb +0 -8
  117. data/test/models/user_test.rb +1 -33
  118. data/test/test_helper.rb +13 -3
  119. metadata +125 -32
  120. data/config/initializers/devise.rb +0 -198
  121. data/test/dummy/config/initializers/assets.rb +0 -10
  122. data/test/dummy/tmp/generators/app/views/devise/mailer/confirmation_instructions.html.erb +0 -5
  123. data/test/dummy/tmp/generators/app/views/devise/mailer/reset_password_instructions.html.erb +0 -8
  124. /data/test/dummy/app/{models → active_record}/lockable_user.rb +0 -0
  125. /data/test/dummy/app/{models → active_record}/mang.rb +0 -0
  126. /data/test/dummy/app/{models → active_record}/only_email_user.rb +0 -0
@@ -2,23 +2,13 @@
2
2
 
3
3
  module DeviseTokenAuth
4
4
  class PasswordsController < DeviseTokenAuth::ApplicationController
5
- before_action :set_user_by_token, only: [:update]
5
+ before_action :validate_redirect_url_param, only: [:create, :edit]
6
6
  skip_after_action :update_auth_header, only: [:create, :edit]
7
7
 
8
- # this action is responsible for generating password reset tokens and
9
- # sending emails
8
+ # this action is responsible for generating password reset tokens and sending emails
10
9
  def create
11
10
  return render_create_error_missing_email unless resource_params[:email]
12
11
 
13
- # give redirect value from params priority
14
- @redirect_url = params.fetch(
15
- :redirect_url,
16
- DeviseTokenAuth.default_password_reset_url
17
- )
18
-
19
- return render_create_error_missing_redirect_url unless @redirect_url
20
- return render_create_error_not_allowed_redirect_url if blacklisted_redirect_url?
21
-
22
12
  @email = get_case_insensitive_field_from_resource_params(:email)
23
13
  @resource = find_resource(:uid, @email)
24
14
 
@@ -44,14 +34,13 @@ module DeviseTokenAuth
44
34
  # this is where users arrive after visiting the password reset confirmation link
45
35
  def edit
46
36
  # if a user is not found, return nil
47
- @resource = with_reset_password_token(resource_params[:reset_password_token])
37
+ @resource = resource_class.with_reset_password_token(resource_params[:reset_password_token])
48
38
 
49
39
  if @resource && @resource.reset_password_period_valid?
50
- client_id, token = @resource.create_token
40
+ token = @resource.create_token unless require_client_password_reset_token?
51
41
 
52
42
  # ensure that user is confirmed
53
43
  @resource.skip_confirmation! if confirmable_enabled? && !@resource.confirmed_at
54
-
55
44
  # allow user to change password once without current_password
56
45
  @resource.allow_password_change = true if recoverable_enabled?
57
46
 
@@ -59,12 +48,16 @@ module DeviseTokenAuth
59
48
 
60
49
  yield @resource if block_given?
61
50
 
62
- redirect_header_options = { reset_password: true }
63
- redirect_headers = build_redirect_headers(token,
64
- client_id,
65
- redirect_header_options)
66
- redirect_to(@resource.build_auth_url(params[:redirect_url],
67
- redirect_headers))
51
+ if require_client_password_reset_token?
52
+ redirect_to DeviseTokenAuth::Url.generate(@redirect_url, reset_password_token: resource_params[:reset_password_token])
53
+ else
54
+ redirect_header_options = { reset_password: true }
55
+ redirect_headers = build_redirect_headers(token.token,
56
+ token.client,
57
+ redirect_header_options)
58
+ redirect_to(@resource.build_auth_url(@redirect_url,
59
+ redirect_headers))
60
+ end
68
61
  else
69
62
  render_edit_error
70
63
  end
@@ -72,6 +65,15 @@ module DeviseTokenAuth
72
65
 
73
66
  def update
74
67
  # make sure user is authorized
68
+ if require_client_password_reset_token? && resource_params[:reset_password_token]
69
+ @resource = resource_class.with_reset_password_token(resource_params[:reset_password_token])
70
+ return render_update_error_unauthorized unless @resource
71
+
72
+ @token = @resource.create_token
73
+ else
74
+ @resource = set_user_by_token
75
+ end
76
+
75
77
  return render_update_error_unauthorized unless @resource
76
78
 
77
79
  # make sure account doesn't use oauth2 provider
@@ -98,9 +100,9 @@ module DeviseTokenAuth
98
100
  protected
99
101
 
100
102
  def resource_update_method
101
- allow_password_change = recoverable_enabled? && @resource.allow_password_change == true
103
+ allow_password_change = recoverable_enabled? && @resource.allow_password_change == true || require_client_password_reset_token?
102
104
  if DeviseTokenAuth.check_current_password_before_update == false || allow_password_change
103
- 'update_attributes'
105
+ 'update'
104
106
  else
105
107
  'update_with_password'
106
108
  end
@@ -114,7 +116,7 @@ module DeviseTokenAuth
114
116
  render_error(401, I18n.t('devise_token_auth.passwords.missing_redirect_url'))
115
117
  end
116
118
 
117
- def render_create_error_not_allowed_redirect_url
119
+ def render_error_not_allowed_redirect_url
118
120
  response = {
119
121
  status: 'error',
120
122
  data: resource_data
@@ -178,15 +180,27 @@ module DeviseTokenAuth
178
180
  params.permit(*params_for_resource(:account_update))
179
181
  end
180
182
 
181
- def with_reset_password_token token
182
- recoverable = resource_class.with_reset_password_token(token)
183
+ def render_not_found_error
184
+ render_error(404, I18n.t('devise_token_auth.passwords.user_not_found', email: @email))
185
+ end
186
+
187
+ def validate_redirect_url_param
188
+ # give redirect value from params priority
189
+ @redirect_url = params.fetch(
190
+ :redirect_url,
191
+ DeviseTokenAuth.default_password_reset_url
192
+ )
183
193
 
184
- recoverable.reset_password_token = token if recoverable && recoverable.reset_password_token.present?
185
- recoverable
194
+ return render_create_error_missing_redirect_url unless @redirect_url
195
+ return render_error_not_allowed_redirect_url if blacklisted_redirect_url?(@redirect_url)
186
196
  end
187
197
 
188
- def render_not_found_error
189
- render_error(404, I18n.t('devise_token_auth.passwords.user_not_found', email: @email))
198
+ def reset_password_token_as_raw?(recoverable)
199
+ recoverable && recoverable.reset_password_token.present? && !require_client_password_reset_token?
200
+ end
201
+
202
+ def require_client_password_reset_token?
203
+ DeviseTokenAuth.require_client_password_reset_token
190
204
  end
191
205
  end
192
206
  end
@@ -28,42 +28,40 @@ module DeviseTokenAuth
28
28
  end
29
29
 
30
30
  # if whitelist is set, validate redirect_url against whitelist
31
- return render_create_error_redirect_url_not_allowed if blacklisted_redirect_url?
31
+ return render_create_error_redirect_url_not_allowed if blacklisted_redirect_url?(@redirect_url)
32
32
 
33
- begin
34
- # override email confirmation, must be sent manually from ctrl
35
- resource_class.set_callback('create', :after, :send_on_create_confirmation_instructions)
36
- resource_class.skip_callback('create', :after, :send_on_create_confirmation_instructions)
33
+ # override email confirmation, must be sent manually from ctrl
34
+ callback_name = defined?(ActiveRecord) && resource_class < ActiveRecord::Base ? :commit : :create
35
+ resource_class.set_callback(callback_name, :after, :send_on_create_confirmation_instructions)
36
+ resource_class.skip_callback(callback_name, :after, :send_on_create_confirmation_instructions)
37
37
 
38
- if @resource.respond_to? :skip_confirmation_notification!
39
- # Fix duplicate e-mails by disabling Devise confirmation e-mail
40
- @resource.skip_confirmation_notification!
41
- end
38
+ if @resource.respond_to? :skip_confirmation_notification!
39
+ # Fix duplicate e-mails by disabling Devise confirmation e-mail
40
+ @resource.skip_confirmation_notification!
41
+ end
42
42
 
43
- if @resource.save
44
- yield @resource if block_given?
43
+ if @resource.save
44
+ yield @resource if block_given?
45
45
 
46
- if @resource.confirmed?
47
- # email auth has been bypassed, authenticate user
48
- @client_id, @token = @resource.create_token
49
- @resource.save!
50
- update_auth_header
51
- else
52
- # user will require email authentication
53
- @resource.send_confirmation_instructions(
54
- client_config: params[:config_name],
55
- redirect_url: @redirect_url
56
- )
57
- end
58
-
59
- render_create_success
60
- else
61
- clean_up_passwords @resource
62
- render_create_error
46
+ unless @resource.confirmed?
47
+ # user will require email authentication
48
+ @resource.send_confirmation_instructions({
49
+ client_config: params[:config_name],
50
+ redirect_url: @redirect_url
51
+ })
63
52
  end
64
- rescue ActiveRecord::RecordNotUnique
53
+
54
+ if active_for_authentication?
55
+ # email auth has been bypassed, authenticate user
56
+ @token = @resource.create_token
57
+ @resource.save!
58
+ update_auth_header
59
+ end
60
+
61
+ render_create_success
62
+ else
65
63
  clean_up_passwords @resource
66
- render_create_error_email_already_exists
64
+ render_create_error
67
65
  end
68
66
  end
69
67
 
@@ -145,15 +143,6 @@ module DeviseTokenAuth
145
143
  }, status: 422
146
144
  end
147
145
 
148
- def render_create_error_email_already_exists
149
- response = {
150
- status: 'error',
151
- data: resource_data
152
- }
153
- message = I18n.t('devise_token_auth.registrations.email_already_exists', email: @resource.email)
154
- render_error(422, message, response)
155
- end
156
-
157
146
  def render_update_success
158
147
  render json: {
159
148
  status: 'success',
@@ -193,7 +182,7 @@ module DeviseTokenAuth
193
182
  elsif account_update_params.key?(:current_password)
194
183
  'update_with_password'
195
184
  else
196
- 'update_attributes'
185
+ 'update'
197
186
  end
198
187
  end
199
188
 
@@ -208,5 +197,9 @@ module DeviseTokenAuth
208
197
  def validate_post_data which, message
209
198
  render_error(:unprocessable_entity, message, status: 'error') if which.empty?
210
199
  end
200
+
201
+ def active_for_authentication?
202
+ !@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?
203
+ end
211
204
  end
212
205
  end
@@ -26,7 +26,7 @@ module DeviseTokenAuth
26
26
  if (@resource.respond_to?(:valid_for_authentication?) && !@resource.valid_for_authentication? { valid_password }) || !valid_password
27
27
  return render_create_error_bad_credentials
28
28
  end
29
- @client_id, @token = @resource.create_token
29
+ @token = @resource.create_token
30
30
  @resource.save
31
31
 
32
32
  sign_in(:user, @resource, store: false, bypass: false)
@@ -48,11 +48,11 @@ module DeviseTokenAuth
48
48
  def destroy
49
49
  # remove auth instance variables so that after_action does not run
50
50
  user = remove_instance_variable(:@resource) if @resource
51
- client_id = remove_instance_variable(:@client_id) if @client_id
52
- remove_instance_variable(:@token) if @token
51
+ client = @token.client
52
+ @token.clear!
53
53
 
54
- if user && client_id && user.tokens[client_id]
55
- user.tokens.delete(client_id)
54
+ if user && client && user.tokens[client]
55
+ user.tokens.delete(client)
56
56
  user.save!
57
57
 
58
58
  yield user if block_given?
@@ -34,14 +34,14 @@ module DeviseTokenAuth
34
34
  def show
35
35
  @resource = resource_class.unlock_access_by_token(params[:unlock_token])
36
36
 
37
- if @resource && @resource.id
38
- client_id, token = @resource.create_token
37
+ if @resource.persisted?
38
+ token = @resource.create_token
39
39
  @resource.save!
40
40
  yield @resource if block_given?
41
41
 
42
42
  redirect_header_options = { unlock: true }
43
- redirect_headers = build_redirect_headers(token,
44
- client_id,
43
+ redirect_headers = build_redirect_headers(token.token,
44
+ token.client,
45
45
  redirect_header_options)
46
46
  redirect_to(@resource.build_auth_url(after_unlock_path_for(@resource),
47
47
  redirect_headers))
@@ -0,0 +1,14 @@
1
+ module DeviseTokenAuth::Concerns::ActiveRecordSupport
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ serialize :tokens, DeviseTokenAuth::Concerns::TokensSerialization
6
+ end
7
+
8
+ class_methods do
9
+ # It's abstract replacement .find_by
10
+ def dta_find_by(attrs = {})
11
+ find_by(attrs)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ module DeviseTokenAuth::Concerns::ConfirmableSupport
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ # Override standard devise `postpone_email_change?` method
6
+ # for not to use `will_save_change_to_email?` & `email_changed?` methods.
7
+ def postpone_email_change?
8
+ postpone = self.class.reconfirmable &&
9
+ email_value_in_database != email &&
10
+ !@bypass_confirmation_postpone &&
11
+ self.email.present? &&
12
+ (!@skip_reconfirmation_in_callback || !email_value_in_database.nil?)
13
+ @bypass_confirmation_postpone = false
14
+ postpone
15
+ end
16
+ end
17
+
18
+ protected
19
+
20
+ def email_value_in_database
21
+ rails51 = Rails.gem_version >= Gem::Version.new("5.1.x")
22
+ if rails51 && respond_to?(:email_in_database)
23
+ email_in_database
24
+ else
25
+ email_was
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ module DeviseTokenAuth::Concerns::MongoidSupport
2
+ extend ActiveSupport::Concern
3
+
4
+ def as_json(options = {})
5
+ options[:except] = (options[:except] || []) + [:_id]
6
+ hash = super(options)
7
+ hash['id'] = to_param
8
+ hash
9
+ end
10
+
11
+ class_methods do
12
+ # It's abstract replacement .find_by
13
+ def dta_find_by(attrs = {})
14
+ find_by(attrs)
15
+ rescue Mongoid::Errors::DocumentNotFound
16
+ nil
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ module DeviseTokenAuth::Concerns::TokensSerialization
2
+ extend self
3
+ # Serialization hash to json
4
+ def dump(object)
5
+ JSON.generate(object && object.transform_values do |token|
6
+ serialize_updated_at(token).compact
7
+ end.compact)
8
+ end
9
+
10
+ # Deserialization json to hash
11
+ def load(json)
12
+ case json
13
+ when String
14
+ JSON.parse(json)
15
+ when NilClass
16
+ {}
17
+ else
18
+ json
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def serialize_updated_at(token)
25
+ updated_at_key = ['updated_at', :updated_at].find(&token.method(:[]))
26
+
27
+ return token unless token[updated_at_key].respond_to?(:iso8601)
28
+
29
+ token.merge updated_at_key => token[updated_at_key].iso8601
30
+ end
31
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bcrypt'
4
-
5
3
  module DeviseTokenAuth::Concerns::User
6
4
  extend ActiveSupport::Concern
7
5
 
@@ -9,7 +7,7 @@ module DeviseTokenAuth::Concerns::User
9
7
  @token_equality_cache ||= {}
10
8
 
11
9
  key = "#{token_hash}/#{token}"
12
- result = @token_equality_cache[key] ||= (::BCrypt::Password.new(token_hash) == token)
10
+ result = @token_equality_cache[key] ||= DeviseTokenAuth::TokenFactory.token_hash_is_token?(token_hash, token)
13
11
  @token_equality_cache = {} if @token_equality_cache.size > 10000
14
12
  result
15
13
  end
@@ -20,19 +18,21 @@ module DeviseTokenAuth::Concerns::User
20
18
  devise_modules.delete(:omniauthable)
21
19
  else
22
20
  devise :database_authenticatable, :registerable,
23
- :recoverable, :trackable, :validatable, :confirmable
21
+ :recoverable, :validatable, :confirmable
24
22
  end
25
23
 
26
- serialize :tokens, JSON unless tokens_has_json_column_type?
24
+ if const_defined?('ActiveRecord') && ancestors.include?(ActiveRecord::Base)
25
+ include DeviseTokenAuth::Concerns::ActiveRecordSupport
26
+ end
27
+
28
+ if const_defined?('Mongoid') && ancestors.include?(Mongoid::Document)
29
+ include DeviseTokenAuth::Concerns::MongoidSupport
30
+ end
27
31
 
28
32
  if DeviseTokenAuth.default_callbacks
29
33
  include DeviseTokenAuth::Concerns::UserOmniauthCallbacks
30
34
  end
31
35
 
32
- # can't set default on text fields in mysql, simulate here instead.
33
- after_save :set_empty_token_hash
34
- after_initialize :set_empty_token_hash
35
-
36
36
  # get rid of dead tokens
37
37
  before_save :destroy_expired_tokens
38
38
 
@@ -44,6 +44,10 @@ module DeviseTokenAuth::Concerns::User
44
44
  def email_changed?; false; end
45
45
  def will_save_change_to_email?; false; end
46
46
 
47
+ if DeviseTokenAuth.send_confirmation_email && devise_modules.include?(:confirmable)
48
+ include DeviseTokenAuth::Concerns::ConfirmableSupport
49
+ end
50
+
47
51
  def password_required?
48
52
  return false unless provider == 'email'
49
53
  super
@@ -84,39 +88,25 @@ module DeviseTokenAuth::Concerns::User
84
88
  send_devise_notification(:unlock_instructions, raw, opts)
85
89
  raw
86
90
  end
87
- end
88
91
 
89
- def create_token(client_id: nil, token: nil, expiry: nil, **token_extras)
90
- client_id ||= SecureRandom.urlsafe_base64(nil, false)
91
- token ||= SecureRandom.urlsafe_base64(nil, false)
92
- expiry ||= (Time.zone.now + token_lifespan).to_i
92
+ def create_token(client: nil, lifespan: nil, cost: nil, **token_extras)
93
+ token = DeviseTokenAuth::TokenFactory.create(client: client, lifespan: lifespan, cost: cost)
93
94
 
94
- tokens[client_id] = {
95
- token: BCrypt::Password.create(token),
96
- expiry: expiry
97
- }.merge!(token_extras)
95
+ tokens[token.client] = {
96
+ token: token.token_hash,
97
+ expiry: token.expiry
98
+ }.merge!(token_extras)
98
99
 
99
- clean_old_tokens
100
+ clean_old_tokens
100
101
 
101
- [client_id, token, expiry]
102
- end
103
-
104
- module ClassMethods
105
- protected
106
-
107
- def tokens_has_json_column_type?
108
- database_exists? && table_exists? && columns_hash['tokens'] && columns_hash['tokens'].type.in?([:json, :jsonb])
109
- end
110
-
111
- def database_exists?
112
- ActiveRecord::Base.connection_pool.with_connection { |con| con.active? } rescue false
102
+ token
113
103
  end
114
104
  end
115
105
 
116
- def valid_token?(token, client_id = 'default')
117
- return false unless tokens[client_id]
118
- return true if token_is_current?(token, client_id)
119
- return true if token_can_be_reused?(token, client_id)
106
+ def valid_token?(token, client = 'default')
107
+ return false unless tokens[client]
108
+ return true if token_is_current?(token, client)
109
+ return true if token_can_be_reused?(token, client)
120
110
 
121
111
  # return false if none of the above conditions are met
122
112
  false
@@ -126,10 +116,10 @@ module DeviseTokenAuth::Concerns::User
126
116
  # can be passed on from the client
127
117
  def send_confirmation_notification?; false; end
128
118
 
129
- def token_is_current?(token, client_id)
119
+ def token_is_current?(token, client)
130
120
  # ghetto HashWithIndifferentAccess
131
- expiry = tokens[client_id]['expiry'] || tokens[client_id][:expiry]
132
- token_hash = tokens[client_id]['token'] || tokens[client_id][:token]
121
+ expiry = tokens[client]['expiry'] || tokens[client][:expiry]
122
+ token_hash = tokens[client]['token'] || tokens[client][:token]
133
123
 
134
124
  return true if (
135
125
  # ensure that expiry and token are set
@@ -144,53 +134,52 @@ module DeviseTokenAuth::Concerns::User
144
134
  end
145
135
 
146
136
  # allow batch requests to use the previous token
147
- def token_can_be_reused?(token, client_id)
137
+ def token_can_be_reused?(token, client)
148
138
  # ghetto HashWithIndifferentAccess
149
- updated_at = tokens[client_id]['updated_at'] || tokens[client_id][:updated_at]
150
- last_token = tokens[client_id]['last_token'] || tokens[client_id][:last_token]
139
+ updated_at = tokens[client]['updated_at'] || tokens[client][:updated_at]
140
+ last_token_hash = tokens[client]['last_token'] || tokens[client][:last_token]
151
141
 
152
142
  return true if (
153
143
  # ensure that the last token and its creation time exist
154
- updated_at && last_token &&
144
+ updated_at && last_token_hash &&
155
145
 
156
146
  # ensure that previous token falls within the batch buffer throttle time of the last request
157
- Time.parse(updated_at) > Time.zone.now - DeviseTokenAuth.batch_request_buffer_throttle &&
147
+ updated_at.to_time > Time.zone.now - DeviseTokenAuth.batch_request_buffer_throttle &&
158
148
 
159
149
  # ensure that the token is valid
160
- ::BCrypt::Password.new(last_token) == token
150
+ DeviseTokenAuth::TokenFactory.token_hash_is_token?(last_token_hash, token)
161
151
  )
162
152
  end
163
153
 
164
154
  # update user's auth token (should happen on each request)
165
- def create_new_auth_token(client_id = nil)
155
+ def create_new_auth_token(client = nil)
166
156
  now = Time.zone.now
167
157
 
168
- client_id, token = create_token(
169
- client_id: client_id,
170
- expiry: (now + token_lifespan).to_i,
171
- last_token: tokens.fetch(client_id, {})['token'],
158
+ token = create_token(
159
+ client: client,
160
+ last_token: tokens.fetch(client, {})['token'],
172
161
  updated_at: now
173
162
  )
174
163
 
175
- update_auth_header(token, client_id)
164
+ update_auth_header(token.token, token.client)
176
165
  end
177
166
 
178
- def build_auth_header(token, client_id = 'default')
167
+ def build_auth_header(token, client = 'default')
179
168
  # client may use expiry to prevent validation request if expired
180
169
  # must be cast as string or headers will break
181
- expiry = tokens[client_id]['expiry'] || tokens[client_id][:expiry]
170
+ expiry = tokens[client]['expiry'] || tokens[client][:expiry]
182
171
 
183
172
  {
184
173
  DeviseTokenAuth.headers_names[:"access-token"] => token,
185
174
  DeviseTokenAuth.headers_names[:"token-type"] => 'Bearer',
186
- DeviseTokenAuth.headers_names[:"client"] => client_id,
175
+ DeviseTokenAuth.headers_names[:"client"] => client,
187
176
  DeviseTokenAuth.headers_names[:"expiry"] => expiry.to_s,
188
177
  DeviseTokenAuth.headers_names[:"uid"] => uid
189
178
  }
190
179
  end
191
180
 
192
- def update_auth_header(token, client_id = 'default')
193
- headers = build_auth_header(token, client_id)
181
+ def update_auth_header(token, client = 'default')
182
+ headers = build_auth_header(token, client)
194
183
  clean_old_tokens
195
184
  save!
196
185
 
@@ -204,9 +193,9 @@ module DeviseTokenAuth::Concerns::User
204
193
  DeviseTokenAuth::Url.generate(base_url, args)
205
194
  end
206
195
 
207
- def extend_batch_buffer(token, client_id)
208
- tokens[client_id]['updated_at'] = Time.zone.now
209
- update_auth_header(token, client_id)
196
+ def extend_batch_buffer(token, client)
197
+ tokens[client]['updated_at'] = Time.zone.now
198
+ update_auth_header(token, client)
210
199
  end
211
200
 
212
201
  def confirmed?
@@ -217,16 +206,8 @@ module DeviseTokenAuth::Concerns::User
217
206
  as_json(except: %i[tokens created_at updated_at])
218
207
  end
219
208
 
220
- def token_lifespan
221
- DeviseTokenAuth.token_lifespan
222
- end
223
-
224
209
  protected
225
210
 
226
- def set_empty_token_hash
227
- self.tokens ||= {} if has_attribute?(:tokens)
228
- end
229
-
230
211
  def destroy_expired_tokens
231
212
  if tokens
232
213
  tokens.delete_if do |cid, v|
@@ -237,11 +218,11 @@ module DeviseTokenAuth::Concerns::User
237
218
  end
238
219
 
239
220
  def should_remove_tokens_after_password_reset?
240
- if Rails::VERSION::MAJOR <= 5
221
+ if Rails::VERSION::MAJOR <= 5 ||defined?('Mongoid')
241
222
  encrypted_password_changed? &&
242
223
  DeviseTokenAuth.remove_tokens_after_password_reset
243
224
  else
244
- saved_change_to_encrypted_password? &&
225
+ saved_change_to_attribute?(:encrypted_password) &&
245
226
  DeviseTokenAuth.remove_tokens_after_password_reset
246
227
  end
247
228
  end
@@ -250,8 +231,8 @@ module DeviseTokenAuth::Concerns::User
250
231
  return unless should_remove_tokens_after_password_reset?
251
232
 
252
233
  if tokens.present? && tokens.many?
253
- client_id, token_data = tokens.max_by { |cid, v| v[:expiry] || v['expiry'] }
254
- self.tokens = { client_id => token_data }
234
+ client, token_data = tokens.max_by { |cid, v| v[:expiry] || v['expiry'] }
235
+ self.tokens = { client => token_data }
255
236
  end
256
237
  end
257
238
 
@@ -5,11 +5,11 @@ module DeviseTokenAuth::Concerns::UserOmniauthCallbacks
5
5
 
6
6
  included do
7
7
  validates :email, presence: true,if: :email_provider?
8
- validates :email, email: true, allow_nil: true, allow_blank: true, if: :email_provider?
8
+ validates :email, :devise_token_auth_email => true, allow_nil: true, allow_blank: true, if: :email_provider?
9
9
  validates_presence_of :uid, unless: :email_provider?
10
10
 
11
11
  # only validate unique emails among email registration users
12
- validates :email, uniqueness: { scope: :provider }, on: :create, if: :email_provider?
12
+ validates :email, uniqueness: { case_sensitive: false, scope: :provider }, on: :create, if: :email_provider?
13
13
 
14
14
  # keep uid in sync with email
15
15
  before_save :sync_uid
@@ -23,6 +23,9 @@ module DeviseTokenAuth::Concerns::UserOmniauthCallbacks
23
23
  end
24
24
 
25
25
  def sync_uid
26
- self.uid = email if provider == 'email'
26
+ if devise_modules.include?(:confirmable) && !@bypass_confirmation_postpone
27
+ return if postpone_email_change?
28
+ end
29
+ self.uid = email if email_provider?
27
30
  end
28
31
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class EmailValidator < ActiveModel::EachValidator
3
+ class DeviseTokenAuthEmailValidator < ActiveModel::EachValidator
4
4
  def validate_each(record, attribute, value)
5
5
  unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
6
- record.errors[attribute] << email_invalid_message
6
+ record.errors.add(attribute, email_invalid_message)
7
7
  end
8
8
  end
9
9