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.
- checksums.yaml +7 -0
- data/LICENSE +13 -0
- data/README.md +99 -0
- data/Rakefile +42 -0
- data/app/controllers/devise_jwt_auth/application_controller.rb +80 -0
- data/app/controllers/devise_jwt_auth/concerns/resource_finder.rb +44 -0
- data/app/controllers/devise_jwt_auth/concerns/set_user_by_jwt_token.rb +111 -0
- data/app/controllers/devise_jwt_auth/confirmations_controller.rb +88 -0
- data/app/controllers/devise_jwt_auth/omniauth_callbacks_controller.rb +291 -0
- data/app/controllers/devise_jwt_auth/passwords_controller.rb +217 -0
- data/app/controllers/devise_jwt_auth/refresh_token_controller.rb +41 -0
- data/app/controllers/devise_jwt_auth/registrations_controller.rb +203 -0
- data/app/controllers/devise_jwt_auth/sessions_controller.rb +131 -0
- data/app/controllers/devise_jwt_auth/unlocks_controller.rb +99 -0
- data/app/models/devise_jwt_auth/concerns/active_record_support.rb +16 -0
- data/app/models/devise_jwt_auth/concerns/confirmable_support.rb +27 -0
- data/app/models/devise_jwt_auth/concerns/mongoid_support.rb +19 -0
- data/app/models/devise_jwt_auth/concerns/tokens_serialization.rb +19 -0
- data/app/models/devise_jwt_auth/concerns/user.rb +117 -0
- data/app/models/devise_jwt_auth/concerns/user_omniauth_callbacks.rb +28 -0
- data/app/validators/devise_jwt_auth_email_validator.rb +23 -0
- data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
- data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
- data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
- data/app/views/devise_jwt_auth/omniauth_external_window.html.erb +38 -0
- data/config/locales/da-DK.yml +52 -0
- data/config/locales/de.yml +51 -0
- data/config/locales/en.yml +57 -0
- data/config/locales/es.yml +51 -0
- data/config/locales/fr.yml +51 -0
- data/config/locales/he.yml +52 -0
- data/config/locales/it.yml +48 -0
- data/config/locales/ja.yml +48 -0
- data/config/locales/ko.yml +51 -0
- data/config/locales/nl.yml +32 -0
- data/config/locales/pl.yml +50 -0
- data/config/locales/pt-BR.yml +48 -0
- data/config/locales/pt.yml +50 -0
- data/config/locales/ro.yml +48 -0
- data/config/locales/ru.yml +52 -0
- data/config/locales/sq.yml +48 -0
- data/config/locales/sv.yml +52 -0
- data/config/locales/uk.yml +61 -0
- data/config/locales/vi.yml +52 -0
- data/config/locales/zh-CN.yml +48 -0
- data/config/locales/zh-HK.yml +50 -0
- data/config/locales/zh-TW.yml +50 -0
- data/lib/devise_jwt_auth.rb +14 -0
- data/lib/devise_jwt_auth/blacklist.rb +2 -0
- data/lib/devise_jwt_auth/controllers/helpers.rb +161 -0
- data/lib/devise_jwt_auth/controllers/url_helpers.rb +10 -0
- data/lib/devise_jwt_auth/engine.rb +96 -0
- data/lib/devise_jwt_auth/errors.rb +8 -0
- data/lib/devise_jwt_auth/rails/routes.rb +118 -0
- data/lib/devise_jwt_auth/token_factory.rb +51 -0
- data/lib/devise_jwt_auth/url.rb +44 -0
- data/lib/devise_jwt_auth/version.rb +5 -0
- data/lib/generators/devise_jwt_auth/USAGE +31 -0
- data/lib/generators/devise_jwt_auth/install_generator.rb +91 -0
- data/lib/generators/devise_jwt_auth/install_generator_helpers.rb +98 -0
- data/lib/generators/devise_jwt_auth/install_mongoid_generator.rb +46 -0
- data/lib/generators/devise_jwt_auth/install_views_generator.rb +18 -0
- data/lib/generators/devise_jwt_auth/templates/devise_jwt_auth.rb +74 -0
- data/lib/generators/devise_jwt_auth/templates/devise_jwt_auth_create_users.rb.erb +51 -0
- data/lib/generators/devise_jwt_auth/templates/user.rb.erb +9 -0
- data/lib/generators/devise_jwt_auth/templates/user_mongoid.rb.erb +56 -0
- data/lib/tasks/devise_token_auth_tasks.rake +6 -0
- data/test/controllers/custom/custom_confirmations_controller_test.rb +25 -0
- data/test/controllers/custom/custom_omniauth_callbacks_controller_test.rb +33 -0
- data/test/controllers/custom/custom_passwords_controller_test.rb +79 -0
- data/test/controllers/custom/custom_refresh_token_controller_test.rb +36 -0
- data/test/controllers/custom/custom_registrations_controller_test.rb +59 -0
- data/test/controllers/custom/custom_sessions_controller_test.rb +39 -0
- data/test/controllers/demo_group_controller_test.rb +150 -0
- data/test/controllers/demo_mang_controller_test.rb +286 -0
- data/test/controllers/demo_user_controller_test.rb +650 -0
- data/test/controllers/devise_jwt_auth/confirmations_controller_test.rb +194 -0
- data/test/controllers/devise_jwt_auth/omniauth_callbacks_controller_test.rb +462 -0
- data/test/controllers/devise_jwt_auth/passwords_controller_test.rb +881 -0
- data/test/controllers/devise_jwt_auth/refresh_token_controller_test.rb +84 -0
- data/test/controllers/devise_jwt_auth/registrations_controller_test.rb +944 -0
- data/test/controllers/devise_jwt_auth/sessions_controller_test.rb +510 -0
- data/test/controllers/devise_jwt_auth/unlocks_controller_test.rb +197 -0
- data/test/controllers/overrides/confirmations_controller_test.rb +47 -0
- data/test/controllers/overrides/omniauth_callbacks_controller_test.rb +53 -0
- data/test/controllers/overrides/passwords_controller_test.rb +65 -0
- data/test/controllers/overrides/refresh_token_controller_test.rb +37 -0
- data/test/controllers/overrides/registrations_controller_test.rb +47 -0
- data/test/controllers/overrides/sessions_controller_test.rb +35 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/app/active_record/confirmable_user.rb +11 -0
- data/test/dummy/app/active_record/lockable_user.rb +7 -0
- data/test/dummy/app/active_record/mang.rb +5 -0
- data/test/dummy/app/active_record/only_email_user.rb +7 -0
- data/test/dummy/app/active_record/scoped_user.rb +9 -0
- data/test/dummy/app/active_record/unconfirmable_user.rb +9 -0
- data/test/dummy/app/active_record/unregisterable_user.rb +9 -0
- data/test/dummy/app/active_record/user.rb +6 -0
- data/test/dummy/app/controllers/application_controller.rb +18 -0
- data/test/dummy/app/controllers/auth_origin_controller.rb +7 -0
- data/test/dummy/app/controllers/custom/confirmations_controller.rb +13 -0
- data/test/dummy/app/controllers/custom/omniauth_callbacks_controller.rb +13 -0
- data/test/dummy/app/controllers/custom/passwords_controller.rb +39 -0
- data/test/dummy/app/controllers/custom/refresh_token_controller.rb +20 -0
- data/test/dummy/app/controllers/custom/registrations_controller.rb +39 -0
- data/test/dummy/app/controllers/custom/sessions_controller.rb +29 -0
- data/test/dummy/app/controllers/demo_group_controller.rb +15 -0
- data/test/dummy/app/controllers/demo_mang_controller.rb +14 -0
- data/test/dummy/app/controllers/demo_user_controller.rb +27 -0
- data/test/dummy/app/controllers/overrides/confirmations_controller.rb +37 -0
- data/test/dummy/app/controllers/overrides/omniauth_callbacks_controller.rb +16 -0
- data/test/dummy/app/controllers/overrides/passwords_controller.rb +45 -0
- data/test/dummy/app/controllers/overrides/refresh_token_controller.rb +22 -0
- data/test/dummy/app/controllers/overrides/registrations_controller.rb +29 -0
- data/test/dummy/app/controllers/overrides/sessions_controller.rb +36 -0
- data/test/dummy/app/helpers/application_helper.rb +1058 -0
- data/test/dummy/app/models/concerns/favorite_color.rb +19 -0
- data/test/dummy/app/mongoid/confirmable_user.rb +52 -0
- data/test/dummy/app/mongoid/lockable_user.rb +38 -0
- data/test/dummy/app/mongoid/mang.rb +46 -0
- data/test/dummy/app/mongoid/only_email_user.rb +33 -0
- data/test/dummy/app/mongoid/scoped_user.rb +50 -0
- data/test/dummy/app/mongoid/unconfirmable_user.rb +44 -0
- data/test/dummy/app/mongoid/unregisterable_user.rb +47 -0
- data/test/dummy/app/mongoid/user.rb +49 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +18 -0
- data/test/dummy/config/application.rb +48 -0
- data/test/dummy/config/application.yml.bk +0 -0
- data/test/dummy/config/boot.rb +11 -0
- data/test/dummy/config/environment.rb +7 -0
- data/test/dummy/config/environments/development.rb +46 -0
- data/test/dummy/config/environments/production.rb +84 -0
- data/test/dummy/config/environments/test.rb +50 -0
- data/test/dummy/config/initializers/assets.rb +10 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +9 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/devise.rb +290 -0
- data/test/dummy/config/initializers/devise_jwt_auth.rb +55 -0
- data/test/dummy/config/initializers/figaro.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +6 -0
- data/test/dummy/config/initializers/inflections.rb +18 -0
- data/test/dummy/config/initializers/mime_types.rb +6 -0
- data/test/dummy/config/initializers/omniauth.rb +11 -0
- data/test/dummy/config/initializers/session_store.rb +5 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +16 -0
- data/test/dummy/config/routes.rb +57 -0
- data/test/dummy/config/spring.rb +3 -0
- data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +58 -0
- data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +57 -0
- data/test/dummy/db/migrate/20140829044006_add_operating_thetan_to_user.rb +8 -0
- data/test/dummy/db/migrate/20140916224624_add_favorite_color_to_mangs.rb +7 -0
- data/test/dummy/db/migrate/20141222035835_devise_token_auth_create_only_email_users.rb +55 -0
- data/test/dummy/db/migrate/20141222053502_devise_token_auth_create_unregisterable_users.rb +56 -0
- data/test/dummy/db/migrate/20150708104536_devise_token_auth_create_unconfirmable_users.rb +56 -0
- data/test/dummy/db/migrate/20160103235141_devise_token_auth_create_scoped_users.rb +56 -0
- data/test/dummy/db/migrate/20160629184441_devise_token_auth_create_lockable_users.rb +56 -0
- data/test/dummy/db/migrate/20190924101113_devise_token_auth_create_confirmable_users.rb +49 -0
- data/test/dummy/db/schema.rb +198 -0
- data/test/dummy/lib/migration_database_helper.rb +43 -0
- data/test/dummy/tmp/generators/app/models/user.rb +9 -0
- data/test/dummy/tmp/generators/config/initializers/devise_jwt_auth.rb +74 -0
- data/test/dummy/tmp/generators/config/routes.rb +4 -0
- data/test/dummy/tmp/generators/db/migrate/20200206224309_devise_jwt_auth_create_users.rb +51 -0
- data/test/factories/users.rb +41 -0
- data/test/lib/devise_jwt_auth/blacklist_test.rb +11 -0
- data/test/lib/devise_jwt_auth/token_factory_test.rb +115 -0
- data/test/lib/devise_jwt_auth/url_test.rb +26 -0
- data/test/lib/generators/devise_jwt_auth/install_generator_test.rb +219 -0
- data/test/lib/generators/devise_jwt_auth/install_generator_with_namespace_test.rb +224 -0
- data/test/lib/generators/devise_jwt_auth/install_views_generator_test.rb +25 -0
- data/test/models/concerns/mongoid_support_test.rb +31 -0
- data/test/models/concerns/tokens_serialization_test.rb +72 -0
- data/test/models/confirmable_user_test.rb +35 -0
- data/test/models/only_email_user_test.rb +29 -0
- data/test/models/user_test.rb +110 -0
- data/test/support/controllers/routes.rb +43 -0
- data/test/test_helper.rb +91 -0
- 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
|