devise_jwt_auth 0.1.0

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 (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