devise_token_auth 1.0.0 → 1.2.2

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 (134) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +6 -3
  3. data/app/controllers/devise_token_auth/application_controller.rb +23 -3
  4. data/app/controllers/devise_token_auth/concerns/resource_finder.rb +24 -11
  5. data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +78 -57
  6. data/app/controllers/devise_token_auth/confirmations_controller.rb +69 -19
  7. data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +89 -44
  8. data/app/controllers/devise_token_auth/passwords_controller.rb +55 -31
  9. data/app/controllers/devise_token_auth/registrations_controller.rb +33 -40
  10. data/app/controllers/devise_token_auth/sessions_controller.rb +36 -14
  11. data/app/controllers/devise_token_auth/unlocks_controller.rb +12 -7
  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 +79 -80
  17. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +12 -5
  18. data/app/validators/{email_validator.rb → devise_token_auth_email_validator.rb} +11 -3
  19. data/app/views/devise_token_auth/omniauth_external_window.html.erb +1 -1
  20. data/config/locales/da-DK.yml +2 -0
  21. data/config/locales/de.yml +2 -0
  22. data/config/locales/en.yml +10 -0
  23. data/config/locales/es.yml +2 -0
  24. data/config/locales/fr.yml +2 -0
  25. data/config/locales/he.yml +52 -0
  26. data/config/locales/it.yml +2 -0
  27. data/config/locales/ja.yml +16 -2
  28. data/config/locales/ko.yml +51 -0
  29. data/config/locales/nl.yml +2 -0
  30. data/config/locales/pl.yml +6 -3
  31. data/config/locales/pt-BR.yml +2 -0
  32. data/config/locales/pt.yml +6 -3
  33. data/config/locales/ro.yml +2 -0
  34. data/config/locales/ru.yml +2 -0
  35. data/config/locales/sq.yml +2 -0
  36. data/config/locales/sv.yml +2 -0
  37. data/config/locales/uk.yml +2 -0
  38. data/config/locales/vi.yml +2 -0
  39. data/config/locales/zh-CN.yml +2 -0
  40. data/config/locales/zh-HK.yml +2 -0
  41. data/config/locales/zh-TW.yml +2 -0
  42. data/lib/devise_token_auth/blacklist.rb +6 -0
  43. data/lib/devise_token_auth/controllers/helpers.rb +5 -9
  44. data/lib/devise_token_auth/engine.rb +17 -2
  45. data/lib/devise_token_auth/rails/routes.rb +22 -16
  46. data/lib/devise_token_auth/token_factory.rb +126 -0
  47. data/lib/devise_token_auth/url.rb +3 -0
  48. data/lib/devise_token_auth/version.rb +1 -1
  49. data/lib/devise_token_auth.rb +6 -3
  50. data/lib/generators/devise_token_auth/USAGE +1 -1
  51. data/lib/generators/devise_token_auth/install_generator.rb +7 -91
  52. data/lib/generators/devise_token_auth/install_generator_helpers.rb +98 -0
  53. data/lib/generators/devise_token_auth/install_mongoid_generator.rb +46 -0
  54. data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +21 -5
  55. data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +1 -8
  56. data/lib/generators/devise_token_auth/templates/user.rb.erb +2 -2
  57. data/lib/generators/devise_token_auth/templates/user_mongoid.rb.erb +56 -0
  58. data/test/controllers/custom/custom_confirmations_controller_test.rb +2 -2
  59. data/test/controllers/custom/custom_omniauth_callbacks_controller_test.rb +1 -1
  60. data/test/controllers/demo_mang_controller_test.rb +37 -8
  61. data/test/controllers/demo_user_controller_test.rb +39 -10
  62. data/test/controllers/devise_token_auth/confirmations_controller_test.rb +170 -22
  63. data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +117 -53
  64. data/test/controllers/devise_token_auth/passwords_controller_test.rb +299 -122
  65. data/test/controllers/devise_token_auth/registrations_controller_test.rb +56 -16
  66. data/test/controllers/devise_token_auth/sessions_controller_test.rb +139 -75
  67. data/test/controllers/devise_token_auth/token_validations_controller_test.rb +43 -2
  68. data/test/controllers/devise_token_auth/unlocks_controller_test.rb +44 -5
  69. data/test/controllers/overrides/confirmations_controller_test.rb +1 -1
  70. data/test/controllers/overrides/omniauth_callbacks_controller_test.rb +1 -1
  71. data/test/dummy/app/active_record/confirmable_user.rb +11 -0
  72. data/test/dummy/app/{models → active_record}/scoped_user.rb +2 -2
  73. data/test/dummy/app/{models → active_record}/unconfirmable_user.rb +1 -2
  74. data/test/dummy/app/{models → active_record}/unregisterable_user.rb +3 -3
  75. data/test/dummy/app/active_record/user.rb +6 -0
  76. data/test/dummy/app/controllers/application_controller.rb +2 -6
  77. data/test/dummy/app/controllers/overrides/confirmations_controller.rb +5 -4
  78. data/test/dummy/app/controllers/overrides/passwords_controller.rb +5 -4
  79. data/test/dummy/app/controllers/overrides/registrations_controller.rb +1 -1
  80. data/test/dummy/app/controllers/overrides/sessions_controller.rb +2 -2
  81. data/test/dummy/app/models/{user.rb → concerns/favorite_color.rb} +7 -8
  82. data/test/dummy/app/mongoid/confirmable_user.rb +52 -0
  83. data/test/dummy/app/mongoid/lockable_user.rb +38 -0
  84. data/test/dummy/app/mongoid/mang.rb +46 -0
  85. data/test/dummy/app/mongoid/only_email_user.rb +33 -0
  86. data/test/dummy/app/mongoid/scoped_user.rb +50 -0
  87. data/test/dummy/app/mongoid/unconfirmable_user.rb +44 -0
  88. data/test/dummy/app/mongoid/unregisterable_user.rb +47 -0
  89. data/test/dummy/app/mongoid/user.rb +49 -0
  90. data/test/dummy/app/views/layouts/application.html.erb +0 -2
  91. data/test/dummy/config/application.rb +22 -1
  92. data/test/dummy/config/boot.rb +4 -0
  93. data/test/dummy/config/environments/development.rb +0 -10
  94. data/test/dummy/config/environments/production.rb +0 -16
  95. data/test/dummy/config/environments/test.rb +6 -2
  96. data/test/dummy/config/initializers/devise.rb +285 -0
  97. data/test/dummy/config/initializers/devise_token_auth.rb +35 -4
  98. data/test/dummy/config/initializers/figaro.rb +1 -1
  99. data/test/dummy/config/initializers/omniauth.rb +1 -0
  100. data/test/dummy/config/routes.rb +2 -0
  101. data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +0 -7
  102. data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +0 -7
  103. data/test/dummy/db/migrate/20141222035835_devise_token_auth_create_only_email_users.rb +0 -7
  104. data/test/dummy/db/migrate/20141222053502_devise_token_auth_create_unregisterable_users.rb +0 -7
  105. data/test/dummy/db/migrate/20150708104536_devise_token_auth_create_unconfirmable_users.rb +0 -7
  106. data/test/dummy/db/migrate/20160103235141_devise_token_auth_create_scoped_users.rb +0 -7
  107. data/test/dummy/db/migrate/20160629184441_devise_token_auth_create_lockable_users.rb +0 -7
  108. data/test/dummy/db/migrate/20190924101113_devise_token_auth_create_confirmable_users.rb +49 -0
  109. data/test/dummy/db/schema.rb +31 -33
  110. data/test/dummy/tmp/generators/app/models/user.rb +9 -0
  111. data/test/dummy/tmp/generators/config/initializers/devise_token_auth.rb +66 -0
  112. data/test/dummy/tmp/generators/db/migrate/20230415183419_devise_token_auth_create_users.rb +49 -0
  113. data/test/factories/users.rb +3 -2
  114. data/test/lib/devise_token_auth/blacklist_test.rb +19 -0
  115. data/test/lib/devise_token_auth/rails/custom_routes_test.rb +29 -0
  116. data/test/lib/devise_token_auth/rails/routes_test.rb +87 -0
  117. data/test/lib/devise_token_auth/token_factory_test.rb +191 -0
  118. data/test/lib/devise_token_auth/url_test.rb +2 -2
  119. data/test/lib/generators/devise_token_auth/install_generator_test.rb +51 -31
  120. data/test/lib/generators/devise_token_auth/install_generator_with_namespace_test.rb +51 -31
  121. data/test/models/concerns/mongoid_support_test.rb +31 -0
  122. data/test/models/concerns/tokens_serialization_test.rb +104 -0
  123. data/test/models/confirmable_user_test.rb +35 -0
  124. data/test/models/only_email_user_test.rb +0 -8
  125. data/test/models/user_test.rb +13 -23
  126. data/test/test_helper.rb +45 -4
  127. metadata +190 -97
  128. data/config/initializers/devise.rb +0 -198
  129. data/test/dummy/config/initializers/assets.rb +0 -10
  130. data/test/dummy/tmp/generators/app/views/devise/mailer/confirmation_instructions.html.erb +0 -5
  131. data/test/dummy/tmp/generators/app/views/devise/mailer/reset_password_instructions.html.erb +0 -8
  132. /data/test/dummy/app/{models → active_record}/lockable_user.rb +0 -0
  133. /data/test/dummy/app/{models → active_record}/mang.rb +0 -0
  134. /data/test/dummy/app/{models → active_record}/only_email_user.rb +0 -0
@@ -3,6 +3,9 @@
3
3
  module DeviseTokenAuth
4
4
  class OmniauthCallbacksController < DeviseTokenAuth::ApplicationController
5
5
  attr_reader :auth_params
6
+
7
+ before_action :validate_auth_origin_url_param
8
+
6
9
  skip_before_action :set_user_by_token, raise: false
7
10
  skip_after_action :update_auth_header
8
11
 
@@ -12,18 +15,43 @@ module DeviseTokenAuth
12
15
 
13
16
  # derive target redirect route from 'resource_class' param, which was set
14
17
  # before authentication.
15
- devise_mapping = [request.env['omniauth.params']['namespace_name'],
16
- request.env['omniauth.params']['resource_class'].underscore.gsub('/', '_')].compact.join('_')
17
- path = "#{Devise.mappings[devise_mapping.to_sym].fullpath}/#{params[:provider]}/callback"
18
- klass = request.scheme == 'https' ? URI::HTTPS : URI::HTTP
19
- redirect_route = klass.build(host: request.host, port: request.port, path: path).to_s
18
+ devise_mapping = get_devise_mapping
19
+ redirect_route = get_redirect_route(devise_mapping)
20
20
 
21
21
  # preserve omniauth info for success route. ignore 'extra' in twitter
22
22
  # auth response to avoid CookieOverflow.
23
23
  session['dta.omniauth.auth'] = request.env['omniauth.auth'].except('extra')
24
24
  session['dta.omniauth.params'] = request.env['omniauth.params']
25
25
 
26
- redirect_to redirect_route
26
+ redirect_to redirect_route, {status: 307}.merge(redirect_options)
27
+ end
28
+
29
+ def get_redirect_route(devise_mapping)
30
+ path = "#{Devise.mappings[devise_mapping.to_sym].fullpath}/#{params[:provider]}/callback"
31
+ klass = request.scheme == 'https' ? URI::HTTPS : URI::HTTP
32
+ redirect_route = klass.build(host: request.host, port: request.port, path: path).to_s
33
+ end
34
+
35
+ def get_devise_mapping
36
+ # derive target redirect route from 'resource_class' param, which was set
37
+ # before authentication.
38
+ devise_mapping = [request.env['omniauth.params']['namespace_name'],
39
+ request.env['omniauth.params']['resource_class'].underscore.gsub('/', '_')].compact.join('_')
40
+ rescue NoMethodError => err
41
+ default_devise_mapping
42
+ end
43
+
44
+ # This method will only be called if `get_devise_mapping` cannot
45
+ # find the mapping in `omniauth.params`.
46
+ #
47
+ # One example use-case here is for IDP-initiated SAML login. In that
48
+ # case, there will have been no initial request in which to save
49
+ # the devise mapping. If you are in a situation like that, and
50
+ # your app allows for you to determine somehow what the devise
51
+ # mapping should be (because, for example, it is always the same),
52
+ # then you can handle it by overriding this method.
53
+ def default_devise_mapping
54
+ raise NotImplementedError.new('no default_devise_mapping set')
27
55
  end
28
56
 
29
57
  def omniauth_success
@@ -42,6 +70,10 @@ module DeviseTokenAuth
42
70
 
43
71
  yield @resource if block_given?
44
72
 
73
+ if DeviseTokenAuth.cookie_enabled
74
+ set_token_in_cookie(@resource, @token)
75
+ end
76
+
45
77
  render_data_or_redirect('deliverCredentials', @auth_params.as_json, @resource.as_json)
46
78
  end
47
79
 
@@ -50,6 +82,11 @@ module DeviseTokenAuth
50
82
  render_data_or_redirect('authFailure', error: @error)
51
83
  end
52
84
 
85
+ def validate_auth_origin_url_param
86
+ return render_error_not_allowed_auth_origin_url if auth_origin_url && blacklisted_redirect_url?(auth_origin_url)
87
+ end
88
+
89
+
53
90
  protected
54
91
 
55
92
  # this will be determined differently depending on the action that calls
@@ -74,12 +111,12 @@ module DeviseTokenAuth
74
111
  end
75
112
  end
76
113
  @_omniauth_params
77
-
78
114
  end
79
115
 
80
116
  # break out provider attribute assignment for easy method extension
81
117
  def assign_provider_attrs(user, auth_hash)
82
- attrs = auth_hash['info'].slice(*user.attributes.keys)
118
+ attrs = auth_hash['info'].to_hash
119
+ attrs = attrs.slice(*user.attribute_names)
83
120
  user.assign_attributes(attrs)
84
121
  end
85
122
 
@@ -95,25 +132,29 @@ module DeviseTokenAuth
95
132
  end
96
133
 
97
134
  def resource_class(mapping = nil)
98
- if omniauth_params['resource_class']
99
- omniauth_params['resource_class'].constantize
100
- elsif params['resource_class']
101
- params['resource_class'].constantize
102
- else
103
- raise 'No resource_class found'
104
- end
135
+ return @resource_class if defined?(@resource_class)
136
+
137
+ constant_name = omniauth_params['resource_class'].presence || params['resource_class'].presence
138
+ @resource_class = ObjectSpace.each_object(Class).detect { |cls| cls.to_s == constant_name && cls.pretty_print_inspect.starts_with?(constant_name) }
139
+ raise 'No resource_class found' if @resource_class.nil?
140
+
141
+ @resource_class
105
142
  end
106
143
 
107
144
  def resource_name
108
145
  resource_class
109
146
  end
110
147
 
111
- def omniauth_window_type
112
- omniauth_params['omniauth_window_type']
148
+ def unsafe_auth_origin_url
149
+ omniauth_params['auth_origin_url'] || omniauth_params['origin']
113
150
  end
114
151
 
152
+
115
153
  def auth_origin_url
116
- omniauth_params['auth_origin_url'] || omniauth_params['origin']
154
+ if unsafe_auth_origin_url && blacklisted_redirect_url?(unsafe_auth_origin_url)
155
+ return nil
156
+ end
157
+ return unsafe_auth_origin_url
117
158
  end
118
159
 
119
160
  # in the success case, omniauth_window_type is in the omniauth_params.
@@ -122,12 +163,11 @@ module DeviseTokenAuth
122
163
  omniauth_params.nil? ? params['omniauth_window_type'] : omniauth_params['omniauth_window_type']
123
164
  end
124
165
 
125
- # this sesison value is set by the redirect_callbacks method. its purpose
166
+ # this session value is set by the redirect_callbacks method. its purpose
126
167
  # is to persist the omniauth auth hash value thru a redirect. the value
127
- # must be destroyed immediatly after it is accessed by omniauth_success
168
+ # must be destroyed immediately after it is accessed by omniauth_success
128
169
  def auth_hash
129
170
  @_auth_hash ||= session.delete('dta.omniauth.auth')
130
- @_auth_hash
131
171
  end
132
172
 
133
173
  # ensure that this controller responds to :devise_controller? conditionals.
@@ -136,16 +176,6 @@ module DeviseTokenAuth
136
176
  true
137
177
  end
138
178
 
139
- # necessary for access to devise_parameter_sanitizers
140
- def devise_mapping
141
- if omniauth_params
142
- Devise.mappings[[omniauth_params['namespace_name'],
143
- omniauth_params['resource_class'].underscore].compact.join('_').to_sym]
144
- else
145
- request.env['devise.mapping']
146
- end
147
- end
148
-
149
179
  def set_random_password
150
180
  # set crazy password for new oauth users. this is only used to prevent
151
181
  # access via email sign-in.
@@ -156,11 +186,11 @@ module DeviseTokenAuth
156
186
 
157
187
  def create_auth_params
158
188
  @auth_params = {
159
- auth_token: @token,
160
- client_id: @client_id,
161
- uid: @resource.uid,
162
- expiry: @expiry,
163
- config: @config
189
+ auth_token: @token.token,
190
+ client_id: @token.client,
191
+ uid: @resource.uid,
192
+ expiry: @token.expiry,
193
+ config: @config
164
194
  }
165
195
  @auth_params.merge!(oauth_registration: true) if @oauth_registration
166
196
  @auth_params
@@ -168,11 +198,16 @@ module DeviseTokenAuth
168
198
 
169
199
  def set_token_on_resource
170
200
  @config = omniauth_params['config_name']
171
- @client_id, @token, @expiry = @resource.create_token
201
+ @token = @resource.create_token
202
+ end
203
+
204
+ def render_error_not_allowed_auth_origin_url
205
+ message = I18n.t('devise_token_auth.omniauth.not_allowed_redirect_url', redirect_url: unsafe_auth_origin_url)
206
+ render_data_or_redirect('authFailure', error: message)
172
207
  end
173
208
 
174
209
  def render_data(message, data)
175
- @data = data.merge(message: message)
210
+ @data = data.merge(message: ActionController::Base.helpers.sanitize(message))
176
211
  render layout: nil, template: 'devise_token_auth/omniauth_external_window'
177
212
  end
178
213
 
@@ -192,7 +227,7 @@ module DeviseTokenAuth
192
227
  elsif auth_origin_url # default to same-window implementation, which forwards back to auth_origin_url
193
228
 
194
229
  # build and redirect to destination url
195
- redirect_to DeviseTokenAuth::Url.generate(auth_origin_url, data.merge(blank: true))
230
+ redirect_to DeviseTokenAuth::Url.generate(auth_origin_url, data.merge(blank: true).merge(redirect_options))
196
231
  else
197
232
 
198
233
  # there SHOULD always be an auth_origin_url, but if someone does something silly
@@ -209,11 +244,20 @@ module DeviseTokenAuth
209
244
  <html>
210
245
  <head></head>
211
246
  <body>
212
- #{text}
247
+ #{ActionController::Base.helpers.sanitize(text)}
213
248
  </body>
214
249
  </html>)
215
250
  end
216
251
 
252
+ def handle_new_resource
253
+ @oauth_registration = true
254
+ set_random_password
255
+ end
256
+
257
+ def assign_whitelisted_params?
258
+ true
259
+ end
260
+
217
261
  def get_resource_from_auth_hash
218
262
  # find or create user by provider and provider uid
219
263
  @resource = resource_class.where(
@@ -222,16 +266,17 @@ module DeviseTokenAuth
222
266
  ).first_or_initialize
223
267
 
224
268
  if @resource.new_record?
225
- @oauth_registration = true
226
- set_random_password
269
+ handle_new_resource
227
270
  end
228
271
 
229
272
  # sync user info with provider, update/generate auth token
230
273
  assign_provider_attrs(@resource, auth_hash)
231
274
 
232
275
  # assign any additional (whitelisted) attributes
233
- extra_params = whitelisted_params
234
- @resource.assign_attributes(extra_params) if extra_params
276
+ if assign_whitelisted_params?
277
+ extra_params = whitelisted_params
278
+ @resource.assign_attributes(extra_params) if extra_params
279
+ end
235
280
 
236
281
  @resource
237
282
  end
@@ -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,22 @@ 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
+ redirect_options
54
+ else
55
+ if DeviseTokenAuth.cookie_enabled
56
+ set_token_in_cookie(@resource, token)
57
+ end
58
+
59
+ redirect_header_options = { reset_password: true }
60
+ redirect_headers = build_redirect_headers(token.token,
61
+ token.client,
62
+ redirect_header_options)
63
+ redirect_to(@resource.build_auth_url(@redirect_url,
64
+ redirect_headers),
65
+ redirect_options)
66
+ end
68
67
  else
69
68
  render_edit_error
70
69
  end
@@ -72,6 +71,15 @@ module DeviseTokenAuth
72
71
 
73
72
  def update
74
73
  # make sure user is authorized
74
+ if require_client_password_reset_token? && resource_params[:reset_password_token]
75
+ @resource = resource_class.with_reset_password_token(resource_params[:reset_password_token])
76
+ return render_update_error_unauthorized unless @resource
77
+
78
+ @token = @resource.create_token
79
+ else
80
+ @resource = set_user_by_token
81
+ end
82
+
75
83
  return render_update_error_unauthorized unless @resource
76
84
 
77
85
  # make sure account doesn't use oauth2 provider
@@ -98,9 +106,9 @@ module DeviseTokenAuth
98
106
  protected
99
107
 
100
108
  def resource_update_method
101
- allow_password_change = recoverable_enabled? && @resource.allow_password_change == true
109
+ allow_password_change = recoverable_enabled? && @resource.allow_password_change == true || require_client_password_reset_token?
102
110
  if DeviseTokenAuth.check_current_password_before_update == false || allow_password_change
103
- 'update_attributes'
111
+ 'update'
104
112
  else
105
113
  'update_with_password'
106
114
  end
@@ -114,7 +122,7 @@ module DeviseTokenAuth
114
122
  render_error(401, I18n.t('devise_token_auth.passwords.missing_redirect_url'))
115
123
  end
116
124
 
117
- def render_create_error_not_allowed_redirect_url
125
+ def render_error_not_allowed_redirect_url
118
126
  response = {
119
127
  status: 'error',
120
128
  data: resource_data
@@ -126,7 +134,7 @@ module DeviseTokenAuth
126
134
  def render_create_success
127
135
  render json: {
128
136
  success: true,
129
- message: I18n.t('devise_token_auth.passwords.sended', email: @email)
137
+ message: success_message('passwords', @email)
130
138
  }
131
139
  end
132
140
 
@@ -178,15 +186,31 @@ module DeviseTokenAuth
178
186
  params.permit(*params_for_resource(:account_update))
179
187
  end
180
188
 
181
- def with_reset_password_token token
182
- recoverable = resource_class.with_reset_password_token(token)
189
+ def render_not_found_error
190
+ if Devise.paranoid
191
+ render_create_success
192
+ else
193
+ render_error(404, I18n.t('devise_token_auth.passwords.user_not_found', email: @email))
194
+ end
195
+ end
196
+
197
+ def validate_redirect_url_param
198
+ # give redirect value from params priority
199
+ @redirect_url = params.fetch(
200
+ :redirect_url,
201
+ DeviseTokenAuth.default_password_reset_url
202
+ )
183
203
 
184
- recoverable.reset_password_token = token if recoverable && recoverable.reset_password_token.present?
185
- recoverable
204
+ return render_create_error_missing_redirect_url unless @redirect_url
205
+ return render_error_not_allowed_redirect_url if blacklisted_redirect_url?(@redirect_url)
186
206
  end
187
207
 
188
- def render_not_found_error
189
- render_error(404, I18n.t('devise_token_auth.passwords.user_not_found', email: @email))
208
+ def reset_password_token_as_raw?(recoverable)
209
+ recoverable && recoverable.reset_password_token.present? && !require_client_password_reset_token?
210
+ end
211
+
212
+ def require_client_password_reset_token?
213
+ DeviseTokenAuth.require_client_password_reset_token
190
214
  end
191
215
  end
192
216
  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
@@ -11,11 +11,7 @@ module DeviseTokenAuth
11
11
  end
12
12
 
13
13
  def create
14
- # Check
15
- field = (resource_params.keys.map(&:to_sym) & resource_class.authentication_keys).first
16
-
17
- @resource = nil
18
- if field
14
+ if field = (resource_params.keys.map(&:to_sym) & resource_class.authentication_keys).first
19
15
  q_value = get_case_insensitive_field_from_resource_params(field)
20
16
 
21
17
  @resource = find_resource(field, q_value)
@@ -26,21 +22,22 @@ module DeviseTokenAuth
26
22
  if (@resource.respond_to?(:valid_for_authentication?) && !@resource.valid_for_authentication? { valid_password }) || !valid_password
27
23
  return render_create_error_bad_credentials
28
24
  end
29
- @client_id, @token = @resource.create_token
30
- @resource.save
31
25
 
32
- sign_in(:user, @resource, store: false, bypass: false)
26
+ create_and_assign_token
27
+
28
+ sign_in(@resource, scope: :user, store: false, bypass: false)
33
29
 
34
30
  yield @resource if block_given?
35
31
 
36
32
  render_create_success
37
- elsif @resource && !(!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?)
33
+ elsif @resource && !Devise.paranoid && !(!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?)
38
34
  if @resource.respond_to?(:locked_at) && @resource.locked_at
39
35
  render_create_error_account_locked
40
36
  else
41
37
  render_create_error_not_confirmed
42
38
  end
43
39
  else
40
+ hash_password_in_paranoid_mode
44
41
  render_create_error_bad_credentials
45
42
  end
46
43
  end
@@ -48,13 +45,19 @@ module DeviseTokenAuth
48
45
  def destroy
49
46
  # remove auth instance variables so that after_action does not run
50
47
  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
48
+ client = @token.client
49
+ @token.clear!
53
50
 
54
- if user && client_id && user.tokens[client_id]
55
- user.tokens.delete(client_id)
51
+ if user && client && user.tokens[client]
52
+ user.tokens.delete(client)
56
53
  user.save!
57
54
 
55
+ if DeviseTokenAuth.cookie_enabled
56
+ # If a cookie is set with a domain specified then it must be deleted with that domain specified
57
+ # See https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html
58
+ cookies.delete(DeviseTokenAuth.cookie_name, domain: DeviseTokenAuth.cookie_attributes[:domain])
59
+ end
60
+
58
61
  yield user if block_given?
59
62
 
60
63
  render_destroy_success
@@ -72,7 +75,6 @@ module DeviseTokenAuth
72
75
  def get_auth_params
73
76
  auth_key = nil
74
77
  auth_val = nil
75
-
76
78
  # iterate thru allowed auth keys, use first found
77
79
  resource_class.authentication_keys.each do |k|
78
80
  if resource_params[k]
@@ -127,5 +129,25 @@ module DeviseTokenAuth
127
129
  def resource_params
128
130
  params.permit(*params_for_resource(:sign_in))
129
131
  end
132
+
133
+ def create_and_assign_token
134
+ if @resource.respond_to?(:with_lock)
135
+ @resource.with_lock do
136
+ @token = @resource.create_token
137
+ @resource.save!
138
+ end
139
+ else
140
+ @token = @resource.create_token
141
+ @resource.save!
142
+ end
143
+ end
144
+
145
+ def hash_password_in_paranoid_mode
146
+ # In order to avoid timing attacks in paranoid mode, we want the password hash to be
147
+ # calculated even if no resource has been found. Devise's DatabaseAuthenticatable warden
148
+ # strategy handles this case similarly:
149
+ # https://github.com/heartcombo/devise/blob/main/lib/devise/strategies/database_authenticatable.rb
150
+ resource_class.new.password = resource_params[:password] if Devise.paranoid
151
+ end
130
152
  end
131
153
  end
@@ -34,17 +34,18 @@ 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
- redirect_headers))
47
+ redirect_headers),
48
+ redirect_options)
48
49
  else
49
50
  render_show_error
50
51
  end
@@ -63,7 +64,7 @@ module DeviseTokenAuth
63
64
  def render_create_success
64
65
  render json: {
65
66
  success: true,
66
- message: I18n.t('devise_token_auth.unlocks.sended', email: @email)
67
+ message: success_message('unlocks', @email)
67
68
  }
68
69
  end
69
70
 
@@ -79,7 +80,11 @@ module DeviseTokenAuth
79
80
  end
80
81
 
81
82
  def render_not_found_error
82
- render_error(404, I18n.t('devise_token_auth.unlocks.user_not_found', email: @email))
83
+ if Devise.paranoid
84
+ render_create_success
85
+ else
86
+ render_error(404, I18n.t('devise_token_auth.unlocks.user_not_found', email: @email))
87
+ end
83
88
  end
84
89
 
85
90
  def resource_params
@@ -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