devise_jwt_auth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +13 -0
  3. data/README.md +99 -0
  4. data/Rakefile +42 -0
  5. data/app/controllers/devise_jwt_auth/application_controller.rb +80 -0
  6. data/app/controllers/devise_jwt_auth/concerns/resource_finder.rb +44 -0
  7. data/app/controllers/devise_jwt_auth/concerns/set_user_by_jwt_token.rb +111 -0
  8. data/app/controllers/devise_jwt_auth/confirmations_controller.rb +88 -0
  9. data/app/controllers/devise_jwt_auth/omniauth_callbacks_controller.rb +291 -0
  10. data/app/controllers/devise_jwt_auth/passwords_controller.rb +217 -0
  11. data/app/controllers/devise_jwt_auth/refresh_token_controller.rb +41 -0
  12. data/app/controllers/devise_jwt_auth/registrations_controller.rb +203 -0
  13. data/app/controllers/devise_jwt_auth/sessions_controller.rb +131 -0
  14. data/app/controllers/devise_jwt_auth/unlocks_controller.rb +99 -0
  15. data/app/models/devise_jwt_auth/concerns/active_record_support.rb +16 -0
  16. data/app/models/devise_jwt_auth/concerns/confirmable_support.rb +27 -0
  17. data/app/models/devise_jwt_auth/concerns/mongoid_support.rb +19 -0
  18. data/app/models/devise_jwt_auth/concerns/tokens_serialization.rb +19 -0
  19. data/app/models/devise_jwt_auth/concerns/user.rb +117 -0
  20. data/app/models/devise_jwt_auth/concerns/user_omniauth_callbacks.rb +28 -0
  21. data/app/validators/devise_jwt_auth_email_validator.rb +23 -0
  22. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  23. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  24. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  25. data/app/views/devise_jwt_auth/omniauth_external_window.html.erb +38 -0
  26. data/config/locales/da-DK.yml +52 -0
  27. data/config/locales/de.yml +51 -0
  28. data/config/locales/en.yml +57 -0
  29. data/config/locales/es.yml +51 -0
  30. data/config/locales/fr.yml +51 -0
  31. data/config/locales/he.yml +52 -0
  32. data/config/locales/it.yml +48 -0
  33. data/config/locales/ja.yml +48 -0
  34. data/config/locales/ko.yml +51 -0
  35. data/config/locales/nl.yml +32 -0
  36. data/config/locales/pl.yml +50 -0
  37. data/config/locales/pt-BR.yml +48 -0
  38. data/config/locales/pt.yml +50 -0
  39. data/config/locales/ro.yml +48 -0
  40. data/config/locales/ru.yml +52 -0
  41. data/config/locales/sq.yml +48 -0
  42. data/config/locales/sv.yml +52 -0
  43. data/config/locales/uk.yml +61 -0
  44. data/config/locales/vi.yml +52 -0
  45. data/config/locales/zh-CN.yml +48 -0
  46. data/config/locales/zh-HK.yml +50 -0
  47. data/config/locales/zh-TW.yml +50 -0
  48. data/lib/devise_jwt_auth.rb +14 -0
  49. data/lib/devise_jwt_auth/blacklist.rb +2 -0
  50. data/lib/devise_jwt_auth/controllers/helpers.rb +161 -0
  51. data/lib/devise_jwt_auth/controllers/url_helpers.rb +10 -0
  52. data/lib/devise_jwt_auth/engine.rb +96 -0
  53. data/lib/devise_jwt_auth/errors.rb +8 -0
  54. data/lib/devise_jwt_auth/rails/routes.rb +118 -0
  55. data/lib/devise_jwt_auth/token_factory.rb +51 -0
  56. data/lib/devise_jwt_auth/url.rb +44 -0
  57. data/lib/devise_jwt_auth/version.rb +5 -0
  58. data/lib/generators/devise_jwt_auth/USAGE +31 -0
  59. data/lib/generators/devise_jwt_auth/install_generator.rb +91 -0
  60. data/lib/generators/devise_jwt_auth/install_generator_helpers.rb +98 -0
  61. data/lib/generators/devise_jwt_auth/install_mongoid_generator.rb +46 -0
  62. data/lib/generators/devise_jwt_auth/install_views_generator.rb +18 -0
  63. data/lib/generators/devise_jwt_auth/templates/devise_jwt_auth.rb +74 -0
  64. data/lib/generators/devise_jwt_auth/templates/devise_jwt_auth_create_users.rb.erb +51 -0
  65. data/lib/generators/devise_jwt_auth/templates/user.rb.erb +9 -0
  66. data/lib/generators/devise_jwt_auth/templates/user_mongoid.rb.erb +56 -0
  67. data/lib/tasks/devise_token_auth_tasks.rake +6 -0
  68. data/test/controllers/custom/custom_confirmations_controller_test.rb +25 -0
  69. data/test/controllers/custom/custom_omniauth_callbacks_controller_test.rb +33 -0
  70. data/test/controllers/custom/custom_passwords_controller_test.rb +79 -0
  71. data/test/controllers/custom/custom_refresh_token_controller_test.rb +36 -0
  72. data/test/controllers/custom/custom_registrations_controller_test.rb +59 -0
  73. data/test/controllers/custom/custom_sessions_controller_test.rb +39 -0
  74. data/test/controllers/demo_group_controller_test.rb +150 -0
  75. data/test/controllers/demo_mang_controller_test.rb +286 -0
  76. data/test/controllers/demo_user_controller_test.rb +650 -0
  77. data/test/controllers/devise_jwt_auth/confirmations_controller_test.rb +194 -0
  78. data/test/controllers/devise_jwt_auth/omniauth_callbacks_controller_test.rb +462 -0
  79. data/test/controllers/devise_jwt_auth/passwords_controller_test.rb +881 -0
  80. data/test/controllers/devise_jwt_auth/refresh_token_controller_test.rb +84 -0
  81. data/test/controllers/devise_jwt_auth/registrations_controller_test.rb +944 -0
  82. data/test/controllers/devise_jwt_auth/sessions_controller_test.rb +510 -0
  83. data/test/controllers/devise_jwt_auth/unlocks_controller_test.rb +197 -0
  84. data/test/controllers/overrides/confirmations_controller_test.rb +47 -0
  85. data/test/controllers/overrides/omniauth_callbacks_controller_test.rb +53 -0
  86. data/test/controllers/overrides/passwords_controller_test.rb +65 -0
  87. data/test/controllers/overrides/refresh_token_controller_test.rb +37 -0
  88. data/test/controllers/overrides/registrations_controller_test.rb +47 -0
  89. data/test/controllers/overrides/sessions_controller_test.rb +35 -0
  90. data/test/dummy/README.rdoc +28 -0
  91. data/test/dummy/app/active_record/confirmable_user.rb +11 -0
  92. data/test/dummy/app/active_record/lockable_user.rb +7 -0
  93. data/test/dummy/app/active_record/mang.rb +5 -0
  94. data/test/dummy/app/active_record/only_email_user.rb +7 -0
  95. data/test/dummy/app/active_record/scoped_user.rb +9 -0
  96. data/test/dummy/app/active_record/unconfirmable_user.rb +9 -0
  97. data/test/dummy/app/active_record/unregisterable_user.rb +9 -0
  98. data/test/dummy/app/active_record/user.rb +6 -0
  99. data/test/dummy/app/controllers/application_controller.rb +18 -0
  100. data/test/dummy/app/controllers/auth_origin_controller.rb +7 -0
  101. data/test/dummy/app/controllers/custom/confirmations_controller.rb +13 -0
  102. data/test/dummy/app/controllers/custom/omniauth_callbacks_controller.rb +13 -0
  103. data/test/dummy/app/controllers/custom/passwords_controller.rb +39 -0
  104. data/test/dummy/app/controllers/custom/refresh_token_controller.rb +20 -0
  105. data/test/dummy/app/controllers/custom/registrations_controller.rb +39 -0
  106. data/test/dummy/app/controllers/custom/sessions_controller.rb +29 -0
  107. data/test/dummy/app/controllers/demo_group_controller.rb +15 -0
  108. data/test/dummy/app/controllers/demo_mang_controller.rb +14 -0
  109. data/test/dummy/app/controllers/demo_user_controller.rb +27 -0
  110. data/test/dummy/app/controllers/overrides/confirmations_controller.rb +37 -0
  111. data/test/dummy/app/controllers/overrides/omniauth_callbacks_controller.rb +16 -0
  112. data/test/dummy/app/controllers/overrides/passwords_controller.rb +45 -0
  113. data/test/dummy/app/controllers/overrides/refresh_token_controller.rb +22 -0
  114. data/test/dummy/app/controllers/overrides/registrations_controller.rb +29 -0
  115. data/test/dummy/app/controllers/overrides/sessions_controller.rb +36 -0
  116. data/test/dummy/app/helpers/application_helper.rb +1058 -0
  117. data/test/dummy/app/models/concerns/favorite_color.rb +19 -0
  118. data/test/dummy/app/mongoid/confirmable_user.rb +52 -0
  119. data/test/dummy/app/mongoid/lockable_user.rb +38 -0
  120. data/test/dummy/app/mongoid/mang.rb +46 -0
  121. data/test/dummy/app/mongoid/only_email_user.rb +33 -0
  122. data/test/dummy/app/mongoid/scoped_user.rb +50 -0
  123. data/test/dummy/app/mongoid/unconfirmable_user.rb +44 -0
  124. data/test/dummy/app/mongoid/unregisterable_user.rb +47 -0
  125. data/test/dummy/app/mongoid/user.rb +49 -0
  126. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  127. data/test/dummy/config.ru +18 -0
  128. data/test/dummy/config/application.rb +48 -0
  129. data/test/dummy/config/application.yml.bk +0 -0
  130. data/test/dummy/config/boot.rb +11 -0
  131. data/test/dummy/config/environment.rb +7 -0
  132. data/test/dummy/config/environments/development.rb +46 -0
  133. data/test/dummy/config/environments/production.rb +84 -0
  134. data/test/dummy/config/environments/test.rb +50 -0
  135. data/test/dummy/config/initializers/assets.rb +10 -0
  136. data/test/dummy/config/initializers/backtrace_silencers.rb +9 -0
  137. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  138. data/test/dummy/config/initializers/devise.rb +290 -0
  139. data/test/dummy/config/initializers/devise_jwt_auth.rb +55 -0
  140. data/test/dummy/config/initializers/figaro.rb +3 -0
  141. data/test/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  142. data/test/dummy/config/initializers/inflections.rb +18 -0
  143. data/test/dummy/config/initializers/mime_types.rb +6 -0
  144. data/test/dummy/config/initializers/omniauth.rb +11 -0
  145. data/test/dummy/config/initializers/session_store.rb +5 -0
  146. data/test/dummy/config/initializers/wrap_parameters.rb +16 -0
  147. data/test/dummy/config/routes.rb +57 -0
  148. data/test/dummy/config/spring.rb +3 -0
  149. data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +58 -0
  150. data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +57 -0
  151. data/test/dummy/db/migrate/20140829044006_add_operating_thetan_to_user.rb +8 -0
  152. data/test/dummy/db/migrate/20140916224624_add_favorite_color_to_mangs.rb +7 -0
  153. data/test/dummy/db/migrate/20141222035835_devise_token_auth_create_only_email_users.rb +55 -0
  154. data/test/dummy/db/migrate/20141222053502_devise_token_auth_create_unregisterable_users.rb +56 -0
  155. data/test/dummy/db/migrate/20150708104536_devise_token_auth_create_unconfirmable_users.rb +56 -0
  156. data/test/dummy/db/migrate/20160103235141_devise_token_auth_create_scoped_users.rb +56 -0
  157. data/test/dummy/db/migrate/20160629184441_devise_token_auth_create_lockable_users.rb +56 -0
  158. data/test/dummy/db/migrate/20190924101113_devise_token_auth_create_confirmable_users.rb +49 -0
  159. data/test/dummy/db/schema.rb +198 -0
  160. data/test/dummy/lib/migration_database_helper.rb +43 -0
  161. data/test/dummy/tmp/generators/app/models/user.rb +9 -0
  162. data/test/dummy/tmp/generators/config/initializers/devise_jwt_auth.rb +74 -0
  163. data/test/dummy/tmp/generators/config/routes.rb +4 -0
  164. data/test/dummy/tmp/generators/db/migrate/20200206224309_devise_jwt_auth_create_users.rb +51 -0
  165. data/test/factories/users.rb +41 -0
  166. data/test/lib/devise_jwt_auth/blacklist_test.rb +11 -0
  167. data/test/lib/devise_jwt_auth/token_factory_test.rb +115 -0
  168. data/test/lib/devise_jwt_auth/url_test.rb +26 -0
  169. data/test/lib/generators/devise_jwt_auth/install_generator_test.rb +219 -0
  170. data/test/lib/generators/devise_jwt_auth/install_generator_with_namespace_test.rb +224 -0
  171. data/test/lib/generators/devise_jwt_auth/install_views_generator_test.rb +25 -0
  172. data/test/models/concerns/mongoid_support_test.rb +31 -0
  173. data/test/models/concerns/tokens_serialization_test.rb +72 -0
  174. data/test/models/confirmable_user_test.rb +35 -0
  175. data/test/models/only_email_user_test.rb +29 -0
  176. data/test/models/user_test.rb +110 -0
  177. data/test/support/controllers/routes.rb +43 -0
  178. data/test/test_helper.rb +91 -0
  179. metadata +503 -0
@@ -0,0 +1,291 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeviseJwtAuth
4
+ class OmniauthCallbacksController < DeviseJwtAuth::ApplicationController
5
+ attr_reader :auth_params
6
+
7
+ before_action :validate_auth_origin_url_param
8
+
9
+ skip_before_action :set_user_by_jwt_token, raise: false
10
+ # skip_after_action :update_auth_header
11
+
12
+ # intermediary route for successful omniauth authentication. omniauth does
13
+ # not support multiple models, so we must resort to this terrible hack.
14
+ def redirect_callbacks
15
+
16
+ # derive target redirect route from 'resource_class' param, which was set
17
+ # before authentication.
18
+ devise_mapping = get_devise_mapping
19
+ redirect_route = get_redirect_route(devise_mapping)
20
+
21
+ # preserve omniauth info for success route. ignore 'extra' in twitter
22
+ # auth response to avoid CookieOverflow.
23
+ session['dta.omniauth.auth'] = request.env['omniauth.auth'].except('extra')
24
+ session['dta.omniauth.params'] = request.env['omniauth.params']
25
+
26
+ redirect_to redirect_route
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')
55
+ end
56
+
57
+ def omniauth_success
58
+ get_resource_from_auth_hash
59
+ set_token_on_resource
60
+ create_auth_params
61
+
62
+ if confirmable_enabled?
63
+ # don't send confirmation email!!!
64
+ @resource.skip_confirmation!
65
+ end
66
+
67
+ sign_in(:user, @resource, store: false, bypass: false)
68
+
69
+ @resource.save!
70
+
71
+ yield @resource if block_given?
72
+
73
+ render_data_or_redirect('deliverCredentials', @auth_params.as_json, @resource.as_json)
74
+ end
75
+
76
+ def omniauth_failure
77
+ @error = params[:message]
78
+ render_data_or_redirect('authFailure', error: @error)
79
+ end
80
+
81
+ def validate_auth_origin_url_param
82
+ return render_error_not_allowed_auth_origin_url if auth_origin_url && blacklisted_redirect_url?(auth_origin_url)
83
+ end
84
+
85
+
86
+ protected
87
+
88
+ # this will be determined differently depending on the action that calls
89
+ # it. redirect_callbacks is called upon returning from successful omniauth
90
+ # authentication, and the target params live in an omniauth-specific
91
+ # request.env variable. this variable is then persisted thru the redirect
92
+ # using our own dta.omniauth.params session var. the omniauth_success
93
+ # method will access that session var and then destroy it immediately
94
+ # after use. In the failure case, finally, the omniauth params
95
+ # are added as query params in our monkey patch to OmniAuth in engine.rb
96
+ def omniauth_params
97
+ unless defined?(@_omniauth_params)
98
+ if request.env['omniauth.params'] && request.env['omniauth.params'].any?
99
+ @_omniauth_params = request.env['omniauth.params']
100
+ elsif session['dta.omniauth.params'] && session['dta.omniauth.params'].any?
101
+ @_omniauth_params ||= session.delete('dta.omniauth.params')
102
+ @_omniauth_params
103
+ elsif params['omniauth_window_type']
104
+ @_omniauth_params = params.slice('omniauth_window_type', 'auth_origin_url', 'resource_class', 'origin')
105
+ else
106
+ @_omniauth_params = {}
107
+ end
108
+ end
109
+ @_omniauth_params
110
+
111
+ end
112
+
113
+ # break out provider attribute assignment for easy method extension
114
+ def assign_provider_attrs(user, auth_hash)
115
+ attrs = auth_hash['info'].slice(*user.attribute_names)
116
+ user.assign_attributes(attrs)
117
+ end
118
+
119
+ # derive allowed params from the standard devise parameter sanitizer
120
+ def whitelisted_params
121
+ whitelist = params_for_resource(:sign_up)
122
+
123
+ whitelist.inject({}) do |coll, key|
124
+ param = omniauth_params[key.to_s]
125
+ coll[key] = param if param
126
+ coll
127
+ end
128
+ end
129
+
130
+ def resource_class(mapping = nil)
131
+ if omniauth_params['resource_class']
132
+ omniauth_params['resource_class'].constantize
133
+ elsif params['resource_class']
134
+ params['resource_class'].constantize
135
+ else
136
+ raise 'No resource_class found'
137
+ end
138
+ end
139
+
140
+ def resource_name
141
+ resource_class
142
+ end
143
+
144
+ def omniauth_window_type
145
+ omniauth_params['omniauth_window_type']
146
+ end
147
+
148
+ def unsafe_auth_origin_url
149
+ omniauth_params['auth_origin_url'] || omniauth_params['origin']
150
+ end
151
+
152
+
153
+ def auth_origin_url
154
+ if unsafe_auth_origin_url && blacklisted_redirect_url?(unsafe_auth_origin_url)
155
+ return nil
156
+ end
157
+ return unsafe_auth_origin_url
158
+ end
159
+
160
+ # in the success case, omniauth_window_type is in the omniauth_params.
161
+ # in the failure case, it is in a query param. See monkey patch above
162
+ def omniauth_window_type
163
+ omniauth_params.nil? ? params['omniauth_window_type'] : omniauth_params['omniauth_window_type']
164
+ end
165
+
166
+ # this sesison value is set by the redirect_callbacks method. its purpose
167
+ # is to persist the omniauth auth hash value thru a redirect. the value
168
+ # must be destroyed immediatly after it is accessed by omniauth_success
169
+ def auth_hash
170
+ @_auth_hash ||= session.delete('dta.omniauth.auth')
171
+ @_auth_hash
172
+ end
173
+
174
+ # ensure that this controller responds to :devise_controller? conditionals.
175
+ # this is used primarily for access to the parameter sanitizers.
176
+ def assert_is_devise_resource!
177
+ true
178
+ end
179
+
180
+ def set_random_password
181
+ # set crazy password for new oauth users. this is only used to prevent
182
+ # access via email sign-in.
183
+ p = SecureRandom.urlsafe_base64(nil, false)
184
+ @resource.password = p
185
+ @resource.password_confirmation = p
186
+ end
187
+
188
+ def create_auth_params
189
+ @auth_params = @token_header.merge(
190
+ config: @config,
191
+ uid: @resource.uid
192
+ )
193
+ # @auth_params = {
194
+ # auth_token: @token.token,
195
+ # client_id: @token.client,
196
+ # uid: @resource.uid,
197
+ # expiry: @token.expiry,
198
+ # config: @config
199
+ # }
200
+ @auth_params.merge!(oauth_registration: true) if @oauth_registration
201
+ @auth_params
202
+ end
203
+
204
+ def set_token_on_resource
205
+ @config = omniauth_params['config_name']
206
+ # @token = @resource.create_token
207
+ @token_header = @resource.create_named_token_pair
208
+ end
209
+
210
+ def render_error_not_allowed_auth_origin_url
211
+ message = I18n.t('devise_jwt_auth.omniauth.not_allowed_redirect_url', redirect_url: unsafe_auth_origin_url)
212
+ render_data_or_redirect('authFailure', error: message)
213
+ end
214
+
215
+ def render_data(message, data)
216
+ @data = data.merge(message: ActionController::Base.helpers.sanitize(message))
217
+ render layout: nil, template: 'devise_jwt_auth/omniauth_external_window'
218
+ end
219
+
220
+ def render_data_or_redirect(message, data, user_data = {})
221
+
222
+ # We handle inAppBrowser and newWindow the same, but it is nice
223
+ # to support values in case people need custom implementations for each case
224
+ # (For example, nbrustein does not allow new users to be created if logging in with
225
+ # an inAppBrowser)
226
+ #
227
+ # See app/views/devise_jwt_auth/omniauth_external_window.html.erb to understand
228
+ # why we can handle these both the same. The view is setup to handle both cases
229
+ # at the same time.
230
+ if ['inAppBrowser', 'newWindow'].include?(omniauth_window_type)
231
+ render_data(message, user_data.merge(data))
232
+
233
+ elsif auth_origin_url # default to same-window implementation, which forwards back to auth_origin_url
234
+
235
+ # build and redirect to destination url
236
+ redirect_to DeviseJwtAuth::Url.generate(auth_origin_url, data.merge(blank: true))
237
+ else
238
+
239
+ # there SHOULD always be an auth_origin_url, but if someone does something silly
240
+ # like coming straight to this url or refreshing the page at the wrong time, there may not be one.
241
+ # In that case, just render in plain text the error message if there is one or otherwise
242
+ # a generic message.
243
+ fallback_render data[:error] || 'An error occurred'
244
+ end
245
+ end
246
+
247
+ def fallback_render(text)
248
+ render inline: %Q(
249
+
250
+ <html>
251
+ <head></head>
252
+ <body>
253
+ #{ActionController::Base.helpers.sanitize(text)}
254
+ </body>
255
+ </html>)
256
+ end
257
+
258
+ def handle_new_resource
259
+ @oauth_registration = true
260
+ set_random_password
261
+ end
262
+
263
+ def assign_whitelisted_params?
264
+ true
265
+ end
266
+
267
+ def get_resource_from_auth_hash
268
+ # find or create user by provider and provider uid
269
+ @resource = resource_class.where(
270
+ uid: auth_hash['uid'],
271
+ provider: auth_hash['provider']
272
+ ).first_or_initialize
273
+
274
+ if @resource.new_record?
275
+ handle_new_resource
276
+ end
277
+
278
+ # sync user info with provider, update/generate auth token
279
+ assign_provider_attrs(@resource, auth_hash)
280
+
281
+ # assign any additional (whitelisted) attributes
282
+ if assign_whitelisted_params?
283
+ extra_params = whitelisted_params
284
+ @resource.assign_attributes(extra_params) if extra_params
285
+ end
286
+
287
+ @resource
288
+ end
289
+ end
290
+
291
+ end
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeviseJwtAuth
4
+ class PasswordsController < DeviseJwtAuth::ApplicationController
5
+ before_action :validate_redirect_url_param, only: [:create, :edit]
6
+ # skip_after_action :update_auth_header, only: [:create, :edit]
7
+
8
+ # this action is responsible for generating password reset tokens and sending emails
9
+ def create
10
+ return render_create_error_missing_email unless resource_params[:email]
11
+
12
+ @email = get_case_insensitive_field_from_resource_params(:email)
13
+ @resource = find_resource(:uid, @email)
14
+
15
+ if @resource
16
+ yield @resource if block_given?
17
+ @resource.send_reset_password_instructions(
18
+ email: @email,
19
+ provider: 'email',
20
+ redirect_url: @redirect_url,
21
+ client_config: params[:config_name]
22
+ )
23
+
24
+ if @resource.errors.empty?
25
+ return render_create_success
26
+ else
27
+ render_create_error @resource.errors
28
+ end
29
+ else
30
+ render_not_found_error
31
+ end
32
+ end
33
+
34
+ # this is where users arrive after visiting the password reset confirmation link
35
+ def edit
36
+ # if a user is not found, return nil
37
+ @resource = resource_class.with_reset_password_token(resource_params[:reset_password_token])
38
+
39
+ if @resource && @resource.reset_password_period_valid?
40
+ # TODO: add a token invalidator
41
+ # token = @resource.create_token unless require_client_password_reset_token?
42
+
43
+ # ensure that user is confirmed
44
+ @resource.skip_confirmation! if confirmable_enabled? && !@resource.confirmed_at
45
+ # allow user to change password once without current_password
46
+ @resource.allow_password_change = true if recoverable_enabled?
47
+ @resource.save!
48
+
49
+ yield @resource if block_given?
50
+
51
+ if require_client_password_reset_token?
52
+ redirect_to DeviseJwtAuth::Url.generate(@redirect_url, reset_password_token: resource_params[:reset_password_token])
53
+ else
54
+ redirect_header_options = { reset_password: true }
55
+ redirect_headers = @resource.create_named_token_pair.
56
+ merge(redirect_header_options)
57
+
58
+ # TODO: do we put the refresh token here?
59
+ # we do if token exists (see line 41)
60
+ update_refresh_token_cookie
61
+
62
+ redirect_to_link = DeviseJwtAuth::Url.generate(@redirect_url, redirect_headers)
63
+
64
+ redirect_to redirect_to_link
65
+ end
66
+ else
67
+ render_edit_error
68
+ end
69
+ end
70
+
71
+ def update
72
+ # make sure user is authorized
73
+ if require_client_password_reset_token? && resource_params[:reset_password_token]
74
+ @resource = resource_class.with_reset_password_token(resource_params[:reset_password_token])
75
+ return render_update_error_unauthorized unless @resource
76
+
77
+ # @token = @resource.create_token
78
+ else
79
+ @resource = set_user_by_token
80
+ end
81
+
82
+ return render_update_error_unauthorized unless @resource
83
+
84
+ # make sure account doesn't use oauth2 provider
85
+ unless @resource.provider == 'email'
86
+ return render_update_error_password_not_required
87
+ end
88
+
89
+ # ensure that password params were sent
90
+ unless password_resource_params[:password] && password_resource_params[:password_confirmation]
91
+ return render_update_error_missing_password
92
+ end
93
+
94
+ if @resource.send(resource_update_method, password_resource_params)
95
+ @resource.allow_password_change = false if recoverable_enabled?
96
+ @resource.save!
97
+
98
+ yield @resource if block_given?
99
+ # invalidate old tokens
100
+ # send refresh cookie
101
+ # send access token
102
+ update_refresh_token_cookie
103
+ return render_update_success
104
+ else
105
+ return render_update_error
106
+ end
107
+ end
108
+
109
+ protected
110
+
111
+ def resource_update_method
112
+ allow_password_change = recoverable_enabled? && @resource.allow_password_change == true || require_client_password_reset_token?
113
+ if DeviseJwtAuth.check_current_password_before_update == false || allow_password_change
114
+ 'update'
115
+ else
116
+ 'update_with_password'
117
+ end
118
+ end
119
+
120
+ def render_create_error_missing_email
121
+ render_error(401, I18n.t('devise_jwt_auth.passwords.missing_email'))
122
+ end
123
+
124
+ def render_create_error_missing_redirect_url
125
+ render_error(401, I18n.t('devise_jwt_auth.passwords.missing_redirect_url'))
126
+ end
127
+
128
+ def render_error_not_allowed_redirect_url
129
+ response = {
130
+ status: 'error',
131
+ data: resource_data
132
+ }
133
+ message = I18n.t('devise_jwt_auth.passwords.not_allowed_redirect_url', redirect_url: @redirect_url)
134
+ render_error(422, message, response)
135
+ end
136
+
137
+ def render_create_success
138
+ render json: {
139
+ success: true,
140
+ message: I18n.t('devise_jwt_auth.passwords.sended', email: @email)
141
+ }
142
+ end
143
+
144
+ def render_create_error(errors)
145
+ render json: {
146
+ success: false,
147
+ errors: errors
148
+ }, status: 400
149
+ end
150
+
151
+ def render_edit_error
152
+ raise ActionController::RoutingError, 'Not Found'
153
+ end
154
+
155
+ def render_update_error_unauthorized
156
+ render_error(401, 'Unauthorized')
157
+ end
158
+
159
+ def render_update_error_password_not_required
160
+ render_error(422, I18n.t('devise_jwt_auth.passwords.password_not_required', provider: @resource.provider.humanize))
161
+ end
162
+
163
+ def render_update_error_missing_password
164
+ render_error(422, I18n.t('devise_jwt_auth.passwords.missing_passwords'))
165
+ end
166
+
167
+ def render_update_success
168
+ response_body = {
169
+ success: true,
170
+ data: resource_data,
171
+ message: I18n.t('devise_jwt_auth.passwords.successfully_updated')
172
+ }.merge!(@resource.create_named_token_pair)
173
+
174
+ render json: response_body
175
+ end
176
+
177
+ def render_update_error
178
+ render json: {
179
+ success: false,
180
+ errors: resource_errors
181
+ }, status: 422
182
+ end
183
+
184
+ private
185
+
186
+ def resource_params
187
+ params.permit(:email, :reset_password_token)
188
+ end
189
+
190
+ def password_resource_params
191
+ params.permit(*params_for_resource(:account_update))
192
+ end
193
+
194
+ def render_not_found_error
195
+ render_error(404, I18n.t('devise_jwt_auth.passwords.user_not_found', email: @email))
196
+ end
197
+
198
+ def validate_redirect_url_param
199
+ # give redirect value from params priority
200
+ @redirect_url = params.fetch(
201
+ :redirect_url,
202
+ DeviseJwtAuth.default_password_reset_url
203
+ )
204
+
205
+ return render_create_error_missing_redirect_url unless @redirect_url
206
+ return render_error_not_allowed_redirect_url if blacklisted_redirect_url?(@redirect_url)
207
+ end
208
+
209
+ def reset_password_token_as_raw?(recoverable)
210
+ recoverable && recoverable.reset_password_token.present? && !require_client_password_reset_token?
211
+ end
212
+
213
+ def require_client_password_reset_token?
214
+ DeviseJwtAuth.require_client_password_reset_token
215
+ end
216
+ end
217
+ end