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