sidecar_token_auth 1.0.1

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 (175) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +13 -0
  3. data/README.md +12 -0
  4. data/Rakefile +42 -0
  5. data/app/controllers/devise_token_auth/application_controller.rb +79 -0
  6. data/app/controllers/devise_token_auth/concerns/resource_finder.rb +44 -0
  7. data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +162 -0
  8. data/app/controllers/devise_token_auth/confirmations_controller.rb +90 -0
  9. data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +287 -0
  10. data/app/controllers/devise_token_auth/passwords_controller.rb +206 -0
  11. data/app/controllers/devise_token_auth/registrations_controller.rb +283 -0
  12. data/app/controllers/devise_token_auth/sessions_controller.rb +245 -0
  13. data/app/controllers/devise_token_auth/token_validations_controller.rb +31 -0
  14. data/app/controllers/devise_token_auth/unlocks_controller.rb +89 -0
  15. data/app/models/devise_token_auth/concerns/active_record_support.rb +16 -0
  16. data/app/models/devise_token_auth/concerns/confirmable_support.rb +28 -0
  17. data/app/models/devise_token_auth/concerns/mongoid_support.rb +19 -0
  18. data/app/models/devise_token_auth/concerns/tokens_serialization.rb +19 -0
  19. data/app/models/devise_token_auth/concerns/user.rb +257 -0
  20. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +31 -0
  21. data/app/validators/devise_token_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_token_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 +60 -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 +51 -0
  37. data/config/locales/pt-BR.yml +48 -0
  38. data/config/locales/pt.yml +51 -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_token_auth.rb +14 -0
  49. data/lib/devise_token_auth/blacklist.rb +2 -0
  50. data/lib/devise_token_auth/controllers/helpers.rb +157 -0
  51. data/lib/devise_token_auth/controllers/url_helpers.rb +10 -0
  52. data/lib/devise_token_auth/engine.rb +96 -0
  53. data/lib/devise_token_auth/errors.rb +8 -0
  54. data/lib/devise_token_auth/rails/routes.rb +116 -0
  55. data/lib/devise_token_auth/token_factory.rb +126 -0
  56. data/lib/devise_token_auth/url.rb +44 -0
  57. data/lib/devise_token_auth/version.rb +5 -0
  58. data/lib/generators/devise_token_auth/USAGE +31 -0
  59. data/lib/generators/devise_token_auth/install_generator.rb +91 -0
  60. data/lib/generators/devise_token_auth/install_generator_helpers.rb +98 -0
  61. data/lib/generators/devise_token_auth/install_mongoid_generator.rb +46 -0
  62. data/lib/generators/devise_token_auth/install_views_generator.rb +18 -0
  63. data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +60 -0
  64. data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +49 -0
  65. data/lib/generators/devise_token_auth/templates/user.rb.erb +9 -0
  66. data/lib/generators/devise_token_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_registrations_controller_test.rb +63 -0
  72. data/test/controllers/custom/custom_sessions_controller_test.rb +39 -0
  73. data/test/controllers/custom/custom_token_validations_controller_test.rb +42 -0
  74. data/test/controllers/demo_group_controller_test.rb +151 -0
  75. data/test/controllers/demo_mang_controller_test.rb +284 -0
  76. data/test/controllers/demo_user_controller_test.rb +629 -0
  77. data/test/controllers/devise_token_auth/confirmations_controller_test.rb +191 -0
  78. data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +441 -0
  79. data/test/controllers/devise_token_auth/passwords_controller_test.rb +780 -0
  80. data/test/controllers/devise_token_auth/registrations_controller_test.rb +907 -0
  81. data/test/controllers/devise_token_auth/sessions_controller_test.rb +503 -0
  82. data/test/controllers/devise_token_auth/token_validations_controller_test.rb +102 -0
  83. data/test/controllers/devise_token_auth/unlocks_controller_test.rb +196 -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 +64 -0
  87. data/test/controllers/overrides/registrations_controller_test.rb +46 -0
  88. data/test/controllers/overrides/sessions_controller_test.rb +35 -0
  89. data/test/controllers/overrides/token_validations_controller_test.rb +43 -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/registrations_controller.rb +39 -0
  105. data/test/dummy/app/controllers/custom/sessions_controller.rb +29 -0
  106. data/test/dummy/app/controllers/custom/token_validations_controller.rb +19 -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 +28 -0
  111. data/test/dummy/app/controllers/overrides/omniauth_callbacks_controller.rb +16 -0
  112. data/test/dummy/app/controllers/overrides/passwords_controller.rb +35 -0
  113. data/test/dummy/app/controllers/overrides/registrations_controller.rb +29 -0
  114. data/test/dummy/app/controllers/overrides/sessions_controller.rb +36 -0
  115. data/test/dummy/app/controllers/overrides/token_validations_controller.rb +23 -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_token_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/factories/users.rb +41 -0
  162. data/test/lib/devise_token_auth/blacklist_test.rb +11 -0
  163. data/test/lib/devise_token_auth/token_factory_test.rb +191 -0
  164. data/test/lib/devise_token_auth/url_test.rb +26 -0
  165. data/test/lib/generators/devise_token_auth/install_generator_test.rb +217 -0
  166. data/test/lib/generators/devise_token_auth/install_generator_with_namespace_test.rb +222 -0
  167. data/test/lib/generators/devise_token_auth/install_views_generator_test.rb +25 -0
  168. data/test/models/concerns/mongoid_support_test.rb +31 -0
  169. data/test/models/concerns/tokens_serialization_test.rb +70 -0
  170. data/test/models/confirmable_user_test.rb +35 -0
  171. data/test/models/only_email_user_test.rb +29 -0
  172. data/test/models/user_test.rb +108 -0
  173. data/test/support/controllers/routes.rb +43 -0
  174. data/test/test_helper.rb +103 -0
  175. metadata +481 -0
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeviseTokenAuth
4
+ class TokenValidationsController < DeviseTokenAuth::ApplicationController
5
+ skip_before_action :assert_is_devise_resource!, only: [:validate_token]
6
+ before_action :set_user_by_token, only: [:validate_token]
7
+
8
+ def validate_token
9
+ # @resource will have been set by set_user_by_token concern
10
+ if @resource
11
+ yield @resource if block_given?
12
+ render_validate_token_success
13
+ else
14
+ render_validate_token_error
15
+ end
16
+ end
17
+
18
+ protected
19
+
20
+ def render_validate_token_success
21
+ render json: {
22
+ success: true,
23
+ data: resource_data(resource_json: @resource.token_validation_response)
24
+ }
25
+ end
26
+
27
+ def render_validate_token_error
28
+ render_error(401, I18n.t('devise_token_auth.token_validations.invalid'))
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeviseTokenAuth
4
+ class UnlocksController < DeviseTokenAuth::ApplicationController
5
+ skip_after_action :update_auth_header, only: [:create, :show]
6
+
7
+ # this action is responsible for generating unlock tokens and
8
+ # 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(:email, @email)
14
+
15
+ if @resource
16
+ yield @resource if block_given?
17
+
18
+ @resource.send_unlock_instructions(
19
+ email: @email,
20
+ provider: 'email',
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
+ def show
35
+ @resource = resource_class.unlock_access_by_token(params[:unlock_token])
36
+
37
+ if @resource.persisted?
38
+ token = @resource.create_token
39
+ @resource.save!
40
+ yield @resource if block_given?
41
+
42
+ redirect_header_options = { unlock: true }
43
+ redirect_headers = build_redirect_headers(token.token,
44
+ token.client,
45
+ redirect_header_options)
46
+ redirect_to(@resource.build_auth_url(after_unlock_path_for(@resource),
47
+ redirect_headers))
48
+ else
49
+ render_show_error
50
+ end
51
+ end
52
+
53
+ private
54
+ def after_unlock_path_for(resource)
55
+ #TODO: This should probably be a configuration option at the very least.
56
+ '/'
57
+ end
58
+
59
+ def render_create_error_missing_email
60
+ render_error(401, I18n.t('devise_token_auth.unlocks.missing_email'))
61
+ end
62
+
63
+ def render_create_success
64
+ render json: {
65
+ success: true,
66
+ message: I18n.t('devise_token_auth.unlocks.sended', email: @email)
67
+ }
68
+ end
69
+
70
+ def render_create_error(errors)
71
+ render json: {
72
+ success: false,
73
+ errors: errors
74
+ }, status: 400
75
+ end
76
+
77
+ def render_show_error
78
+ raise ActionController::RoutingError, 'Not Found'
79
+ end
80
+
81
+ def render_not_found_error
82
+ render_error(404, I18n.t('devise_token_auth.unlocks.user_not_found', email: @email))
83
+ end
84
+
85
+ def resource_params
86
+ params.permit(:email, :unlock_token, :config)
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,16 @@
1
+ require_relative 'tokens_serialization'
2
+
3
+ module DeviseTokenAuth::Concerns::ActiveRecordSupport
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ serialize :tokens, DeviseTokenAuth::Concerns::TokensSerialization
8
+ end
9
+
10
+ class_methods do
11
+ # It's abstract replacement .find_by
12
+ def dta_find_by(attrs = {})
13
+ find_by(attrs)
14
+ end
15
+ end
16
+ 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,19 @@
1
+ module DeviseTokenAuth::Concerns::TokensSerialization
2
+ # Serialization hash to json
3
+ def self.dump(object)
4
+ object.each_value(&:compact!) unless object.nil?
5
+ JSON.generate(object)
6
+ end
7
+
8
+ # Deserialization json to hash
9
+ def self.load(json)
10
+ case json
11
+ when String
12
+ JSON.parse(json)
13
+ when NilClass
14
+ {}
15
+ else
16
+ json
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,257 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeviseTokenAuth::Concerns::User
4
+ extend ActiveSupport::Concern
5
+
6
+ def self.tokens_match?(token_hash, token)
7
+ @token_equality_cache ||= {}
8
+
9
+ key = "#{token_hash}/#{token}"
10
+ result = @token_equality_cache[key] ||= DeviseTokenAuth::TokenFactory.token_hash_is_token?(token_hash, token)
11
+ @token_equality_cache = {} if @token_equality_cache.size > 10000
12
+ result
13
+ end
14
+
15
+ included do
16
+ # Hack to check if devise is already enabled
17
+ if method_defined?(:devise_modules)
18
+ devise_modules.delete(:omniauthable)
19
+ else
20
+ devise :database_authenticatable, :registerable,
21
+ :recoverable, :validatable, :confirmable
22
+ end
23
+
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
31
+
32
+ if DeviseTokenAuth.default_callbacks
33
+ include DeviseTokenAuth::Concerns::UserOmniauthCallbacks
34
+ end
35
+
36
+ # get rid of dead tokens
37
+ before_save :destroy_expired_tokens
38
+
39
+ # remove old tokens if password has changed
40
+ before_save :remove_tokens_after_password_reset
41
+
42
+ # don't use default devise email validation
43
+ def email_required?; false; end
44
+ def email_changed?; false; end
45
+ def will_save_change_to_email?; false; end
46
+
47
+ if DeviseTokenAuth.send_confirmation_email && devise_modules.include?(:confirmable)
48
+ include DeviseTokenAuth::Concerns::ConfirmableSupport
49
+ end
50
+
51
+ def password_required?
52
+ return false unless provider == 'email'
53
+ super
54
+ end
55
+
56
+ # override devise method to include additional info as opts hash
57
+ def send_confirmation_instructions(opts = {})
58
+ generate_confirmation_token! unless @raw_confirmation_token
59
+
60
+ # fall back to "default" config name
61
+ opts[:client_config] ||= 'default'
62
+ opts[:to] = unconfirmed_email if pending_reconfirmation?
63
+ opts[:redirect_url] ||= DeviseTokenAuth.default_confirm_success_url
64
+
65
+ send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
66
+ end
67
+
68
+ # override devise method to include additional info as opts hash
69
+ def send_reset_password_instructions(opts = {})
70
+ token = set_reset_password_token
71
+
72
+ # fall back to "default" config name
73
+ opts[:client_config] ||= 'default'
74
+
75
+ send_devise_notification(:reset_password_instructions, token, opts)
76
+ token
77
+ end
78
+
79
+ # override devise method to include additional info as opts hash
80
+ def send_unlock_instructions(opts = {})
81
+ raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
82
+ self.unlock_token = enc
83
+ save(validate: false)
84
+
85
+ # fall back to "default" config name
86
+ opts[:client_config] ||= 'default'
87
+
88
+ send_devise_notification(:unlock_instructions, raw, opts)
89
+ raw
90
+ end
91
+
92
+ def create_token(client: nil, lifespan: nil, cost: nil, **token_extras)
93
+ token = DeviseTokenAuth::TokenFactory.create(client: client, lifespan: lifespan, cost: cost)
94
+
95
+ tokens[token.client] = {
96
+ token: token.token_hash,
97
+ expiry: token.expiry
98
+ }.merge!(token_extras)
99
+
100
+ clean_old_tokens
101
+
102
+ token
103
+ end
104
+ end
105
+
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)
110
+
111
+ # return false if none of the above conditions are met
112
+ false
113
+ end
114
+
115
+ # this must be done from the controller so that additional params
116
+ # can be passed on from the client
117
+ def send_confirmation_notification?; false; end
118
+
119
+ def token_is_current?(token, client)
120
+ # ghetto HashWithIndifferentAccess
121
+ expiry = tokens[client]['expiry'] || tokens[client][:expiry]
122
+ token_hash = tokens[client]['token'] || tokens[client][:token]
123
+
124
+ return true if (
125
+ # ensure that expiry and token are set
126
+ expiry && token &&
127
+
128
+ # ensure that the token has not yet expired
129
+ DateTime.strptime(expiry.to_s, '%s') > Time.zone.now &&
130
+
131
+ # ensure that the token is valid
132
+ DeviseTokenAuth::Concerns::User.tokens_match?(token_hash, token)
133
+ )
134
+ end
135
+
136
+ # allow batch requests to use the previous token
137
+ def token_can_be_reused?(token, client)
138
+ # ghetto HashWithIndifferentAccess
139
+ updated_at = tokens[client]['updated_at'] || tokens[client][:updated_at]
140
+ last_token_hash = tokens[client]['last_token'] || tokens[client][:last_token]
141
+
142
+ return true if (
143
+ # ensure that the last token and its creation time exist
144
+ updated_at && last_token_hash &&
145
+
146
+ # ensure that previous token falls within the batch buffer throttle time of the last request
147
+ updated_at.to_time > Time.zone.now - DeviseTokenAuth.batch_request_buffer_throttle &&
148
+
149
+ # ensure that the token is valid
150
+ DeviseTokenAuth::TokenFactory.token_hash_is_token?(last_token_hash, token)
151
+ )
152
+ end
153
+
154
+ # update user's auth token (should happen on each request)
155
+ def create_new_auth_token(client = nil)
156
+ now = Time.zone.now
157
+
158
+ token = create_token(
159
+ client: client,
160
+ last_token: tokens.fetch(client, {})['token'],
161
+ updated_at: now.to_s(:rfc822)
162
+ )
163
+
164
+ update_auth_header(token.token, token.client)
165
+ end
166
+
167
+ def build_auth_header(token, client = 'default')
168
+ # client may use expiry to prevent validation request if expired
169
+ # must be cast as string or headers will break
170
+ expiry = tokens[client]['expiry'] || tokens[client][:expiry]
171
+
172
+ {
173
+ DeviseTokenAuth.headers_names[:"access-token"] => token,
174
+ DeviseTokenAuth.headers_names[:"token-type"] => 'Bearer',
175
+ DeviseTokenAuth.headers_names[:"client"] => client,
176
+ DeviseTokenAuth.headers_names[:"expiry"] => expiry.to_s,
177
+ DeviseTokenAuth.headers_names[:"uid"] => uid
178
+ }
179
+ end
180
+
181
+ def update_auth_header(token, client = 'default')
182
+ headers = build_auth_header(token, client)
183
+ clean_old_tokens
184
+ save!
185
+
186
+ headers
187
+ end
188
+
189
+ def build_auth_url(base_url, args)
190
+ args[:uid] = uid
191
+ args[:expiry] = tokens[args[:client_id]]['expiry']
192
+
193
+ DeviseTokenAuth::Url.generate(base_url, args)
194
+ end
195
+
196
+ def extend_batch_buffer(token, client)
197
+ tokens[client]['updated_at'] = Time.zone.now.to_s(:rfc822)
198
+ update_auth_header(token, client)
199
+ end
200
+
201
+ def confirmed?
202
+ devise_modules.exclude?(:confirmable) || super
203
+ end
204
+
205
+ def token_validation_response
206
+ as_json(except: %i[tokens created_at updated_at])
207
+ end
208
+
209
+ protected
210
+
211
+ def destroy_expired_tokens
212
+ if tokens
213
+ tokens.delete_if do |cid, v|
214
+ expiry = v[:expiry] || v['expiry']
215
+ DateTime.strptime(expiry.to_s, '%s') < Time.zone.now
216
+ end
217
+ end
218
+ end
219
+
220
+ def should_remove_tokens_after_password_reset?
221
+ if Rails::VERSION::MAJOR <= 5 ||defined?('Mongoid')
222
+ encrypted_password_changed? &&
223
+ DeviseTokenAuth.remove_tokens_after_password_reset
224
+ else
225
+ saved_change_to_attribute?(:encrypted_password) &&
226
+ DeviseTokenAuth.remove_tokens_after_password_reset
227
+ end
228
+ end
229
+
230
+ def remove_tokens_after_password_reset
231
+ return unless should_remove_tokens_after_password_reset?
232
+
233
+ if tokens.present? && tokens.many?
234
+ client, token_data = tokens.max_by { |cid, v| v[:expiry] || v['expiry'] }
235
+ self.tokens = { client => token_data }
236
+ end
237
+ end
238
+
239
+ def max_client_tokens_exceeded?
240
+ tokens.length > DeviseTokenAuth.max_number_of_devices
241
+ end
242
+
243
+ def clean_old_tokens
244
+ if tokens.present? && max_client_tokens_exceeded?
245
+ # Using Enumerable#sort_by on a Hash will typecast it into an associative
246
+ # Array (i.e. an Array of key-value Array pairs). However, since Hashes
247
+ # have an internal order in Ruby 1.9+, the resulting sorted associative
248
+ # Array can be converted back into a Hash, while maintaining the sorted
249
+ # order.
250
+ self.tokens = tokens.sort_by { |_cid, v| v[:expiry] || v['expiry'] }.to_h
251
+
252
+ # Since the tokens are sorted by expiry, shift the oldest client token
253
+ # off the Hash until it no longer exceeds the maximum number of clients
254
+ tokens.shift while max_client_tokens_exceeded?
255
+ end
256
+ end
257
+ end