devise_token_auth_multi_email 0.9.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 (183) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +13 -0
  3. data/README.md +97 -0
  4. data/Rakefile +42 -0
  5. data/app/controllers/devise_token_auth/application_controller.rb +100 -0
  6. data/app/controllers/devise_token_auth/concerns/resource_finder.rb +68 -0
  7. data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +199 -0
  8. data/app/controllers/devise_token_auth/confirmations_controller.rb +89 -0
  9. data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +284 -0
  10. data/app/controllers/devise_token_auth/passwords_controller.rb +216 -0
  11. data/app/controllers/devise_token_auth/registrations_controller.rb +205 -0
  12. data/app/controllers/devise_token_auth/sessions_controller.rb +153 -0
  13. data/app/controllers/devise_token_auth/token_validations_controller.rb +31 -0
  14. data/app/controllers/devise_token_auth/unlocks_controller.rb +94 -0
  15. data/app/models/devise_token_auth/concerns/active_record_support.rb +18 -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 +31 -0
  19. data/app/models/devise_token_auth/concerns/user.rb +282 -0
  20. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +39 -0
  21. data/app/validators/devise_token_auth_email_validator.rb +31 -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/fa.yml +60 -0
  31. data/config/locales/fr.yml +51 -0
  32. data/config/locales/he.yml +52 -0
  33. data/config/locales/it.yml +48 -0
  34. data/config/locales/ja.yml +60 -0
  35. data/config/locales/ko.yml +51 -0
  36. data/config/locales/nl.yml +32 -0
  37. data/config/locales/pl.yml +51 -0
  38. data/config/locales/pt-BR.yml +48 -0
  39. data/config/locales/pt.yml +51 -0
  40. data/config/locales/ro.yml +48 -0
  41. data/config/locales/ru.yml +52 -0
  42. data/config/locales/sq.yml +48 -0
  43. data/config/locales/sv.yml +52 -0
  44. data/config/locales/uk.yml +61 -0
  45. data/config/locales/vi.yml +52 -0
  46. data/config/locales/zh-CN.yml +48 -0
  47. data/config/locales/zh-HK.yml +50 -0
  48. data/config/locales/zh-TW.yml +50 -0
  49. data/lib/devise_token_auth/blacklist.rb +6 -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 +105 -0
  53. data/lib/devise_token_auth/errors.rb +8 -0
  54. data/lib/devise_token_auth/rails/routes.rb +122 -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/devise_token_auth.rb +14 -0
  59. data/lib/generators/devise_token_auth/USAGE +31 -0
  60. data/lib/generators/devise_token_auth/install_generator.rb +91 -0
  61. data/lib/generators/devise_token_auth/install_generator_helpers.rb +98 -0
  62. data/lib/generators/devise_token_auth/install_mongoid_generator.rb +46 -0
  63. data/lib/generators/devise_token_auth/install_views_generator.rb +18 -0
  64. data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +66 -0
  65. data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +49 -0
  66. data/lib/generators/devise_token_auth/templates/user.rb.erb +9 -0
  67. data/lib/generators/devise_token_auth/templates/user_mongoid.rb.erb +56 -0
  68. data/lib/tasks/devise_token_auth_tasks.rake +6 -0
  69. data/test/controllers/custom/custom_confirmations_controller_test.rb +25 -0
  70. data/test/controllers/custom/custom_omniauth_callbacks_controller_test.rb +33 -0
  71. data/test/controllers/custom/custom_passwords_controller_test.rb +79 -0
  72. data/test/controllers/custom/custom_registrations_controller_test.rb +63 -0
  73. data/test/controllers/custom/custom_sessions_controller_test.rb +39 -0
  74. data/test/controllers/custom/custom_token_validations_controller_test.rb +42 -0
  75. data/test/controllers/demo_group_controller_test.rb +151 -0
  76. data/test/controllers/demo_mang_controller_test.rb +313 -0
  77. data/test/controllers/demo_user_controller_test.rb +658 -0
  78. data/test/controllers/devise_token_auth/confirmations_controller_test.rb +275 -0
  79. data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +438 -0
  80. data/test/controllers/devise_token_auth/passwords_controller_test.rb +893 -0
  81. data/test/controllers/devise_token_auth/registrations_controller_test.rb +920 -0
  82. data/test/controllers/devise_token_auth/sessions_controller_test.rb +605 -0
  83. data/test/controllers/devise_token_auth/token_validations_controller_test.rb +142 -0
  84. data/test/controllers/devise_token_auth/unlocks_controller_test.rb +235 -0
  85. data/test/controllers/overrides/confirmations_controller_test.rb +47 -0
  86. data/test/controllers/overrides/omniauth_callbacks_controller_test.rb +53 -0
  87. data/test/controllers/overrides/passwords_controller_test.rb +64 -0
  88. data/test/controllers/overrides/registrations_controller_test.rb +46 -0
  89. data/test/controllers/overrides/sessions_controller_test.rb +35 -0
  90. data/test/controllers/overrides/token_validations_controller_test.rb +43 -0
  91. data/test/dummy/README.rdoc +28 -0
  92. data/test/dummy/app/active_record/confirmable_user.rb +11 -0
  93. data/test/dummy/app/active_record/lockable_user.rb +7 -0
  94. data/test/dummy/app/active_record/mang.rb +5 -0
  95. data/test/dummy/app/active_record/only_email_user.rb +7 -0
  96. data/test/dummy/app/active_record/scoped_user.rb +9 -0
  97. data/test/dummy/app/active_record/unconfirmable_user.rb +9 -0
  98. data/test/dummy/app/active_record/unregisterable_user.rb +9 -0
  99. data/test/dummy/app/active_record/user.rb +6 -0
  100. data/test/dummy/app/controllers/application_controller.rb +14 -0
  101. data/test/dummy/app/controllers/auth_origin_controller.rb +7 -0
  102. data/test/dummy/app/controllers/custom/confirmations_controller.rb +13 -0
  103. data/test/dummy/app/controllers/custom/omniauth_callbacks_controller.rb +13 -0
  104. data/test/dummy/app/controllers/custom/passwords_controller.rb +39 -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/custom/token_validations_controller.rb +19 -0
  108. data/test/dummy/app/controllers/demo_group_controller.rb +15 -0
  109. data/test/dummy/app/controllers/demo_mang_controller.rb +14 -0
  110. data/test/dummy/app/controllers/demo_user_controller.rb +27 -0
  111. data/test/dummy/app/controllers/overrides/confirmations_controller.rb +29 -0
  112. data/test/dummy/app/controllers/overrides/omniauth_callbacks_controller.rb +16 -0
  113. data/test/dummy/app/controllers/overrides/passwords_controller.rb +36 -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/controllers/overrides/token_validations_controller.rb +23 -0
  117. data/test/dummy/app/helpers/application_helper.rb +1058 -0
  118. data/test/dummy/app/models/concerns/favorite_color.rb +19 -0
  119. data/test/dummy/app/mongoid/confirmable_user.rb +52 -0
  120. data/test/dummy/app/mongoid/lockable_user.rb +38 -0
  121. data/test/dummy/app/mongoid/mang.rb +46 -0
  122. data/test/dummy/app/mongoid/only_email_user.rb +33 -0
  123. data/test/dummy/app/mongoid/scoped_user.rb +50 -0
  124. data/test/dummy/app/mongoid/unconfirmable_user.rb +44 -0
  125. data/test/dummy/app/mongoid/unregisterable_user.rb +47 -0
  126. data/test/dummy/app/mongoid/user.rb +49 -0
  127. data/test/dummy/app/views/layouts/application.html.erb +12 -0
  128. data/test/dummy/config/application.rb +50 -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 +36 -0
  133. data/test/dummy/config/environments/production.rb +68 -0
  134. data/test/dummy/config/environments/test.rb +58 -0
  135. data/test/dummy/config/initializers/backtrace_silencers.rb +9 -0
  136. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  137. data/test/dummy/config/initializers/devise.rb +290 -0
  138. data/test/dummy/config/initializers/devise_token_auth.rb +55 -0
  139. data/test/dummy/config/initializers/figaro.rb +3 -0
  140. data/test/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  141. data/test/dummy/config/initializers/inflections.rb +18 -0
  142. data/test/dummy/config/initializers/mime_types.rb +6 -0
  143. data/test/dummy/config/initializers/omniauth.rb +11 -0
  144. data/test/dummy/config/initializers/session_store.rb +5 -0
  145. data/test/dummy/config/initializers/wrap_parameters.rb +16 -0
  146. data/test/dummy/config/routes.rb +57 -0
  147. data/test/dummy/config/spring.rb +3 -0
  148. data/test/dummy/config.ru +18 -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/mang.rb +9 -0
  162. data/test/dummy/tmp/generators/app/models/user.rb +9 -0
  163. data/test/dummy/tmp/generators/config/initializers/devise_token_auth.rb +60 -0
  164. data/test/dummy/tmp/generators/config/routes.rb +9 -0
  165. data/test/dummy/tmp/generators/db/migrate/20210305040222_devise_token_auth_create_mangs.rb +49 -0
  166. data/test/dummy/tmp/generators/db/migrate/20210305040222_devise_token_auth_create_users.rb +49 -0
  167. data/test/factories/users.rb +41 -0
  168. data/test/lib/devise_token_auth/blacklist_test.rb +19 -0
  169. data/test/lib/devise_token_auth/rails/custom_routes_test.rb +29 -0
  170. data/test/lib/devise_token_auth/rails/routes_test.rb +87 -0
  171. data/test/lib/devise_token_auth/token_factory_test.rb +191 -0
  172. data/test/lib/devise_token_auth/url_test.rb +26 -0
  173. data/test/lib/generators/devise_token_auth/install_generator_test.rb +217 -0
  174. data/test/lib/generators/devise_token_auth/install_generator_with_namespace_test.rb +222 -0
  175. data/test/lib/generators/devise_token_auth/install_views_generator_test.rb +25 -0
  176. data/test/models/concerns/mongoid_support_test.rb +31 -0
  177. data/test/models/concerns/tokens_serialization_test.rb +104 -0
  178. data/test/models/confirmable_user_test.rb +35 -0
  179. data/test/models/only_email_user_test.rb +29 -0
  180. data/test/models/user_test.rb +224 -0
  181. data/test/support/controllers/routes.rb +43 -0
  182. data/test/test_helper.rb +134 -0
  183. metadata +502 -0
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ # see http://www.emilsoman.com/blog/2013/05/18/building-a-tested/
4
+ module DeviseTokenAuth
5
+ class SessionsController < DeviseTokenAuth::ApplicationController
6
+ before_action :set_user_by_token, only: [:destroy]
7
+ after_action :reset_session, only: [:destroy]
8
+
9
+ def new
10
+ render_new_error
11
+ end
12
+
13
+ def create
14
+ if field = (resource_params.keys.map(&:to_sym) & resource_class.authentication_keys).first
15
+ q_value = get_case_insensitive_field_from_resource_params(field)
16
+
17
+ @resource = find_resource(field, q_value)
18
+ end
19
+
20
+ if @resource && valid_params?(field, q_value) && (!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?)
21
+ valid_password = @resource.valid_password?(resource_params[:password])
22
+ if (@resource.respond_to?(:valid_for_authentication?) && !@resource.valid_for_authentication? { valid_password }) || !valid_password
23
+ return render_create_error_bad_credentials
24
+ end
25
+
26
+ create_and_assign_token
27
+
28
+ sign_in(@resource, scope: :user, store: false, bypass: false)
29
+
30
+ yield @resource if block_given?
31
+
32
+ render_create_success
33
+ elsif @resource && !Devise.paranoid && !(!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?)
34
+ if @resource.respond_to?(:locked_at) && @resource.locked_at
35
+ render_create_error_account_locked
36
+ else
37
+ render_create_error_not_confirmed
38
+ end
39
+ else
40
+ hash_password_in_paranoid_mode
41
+ render_create_error_bad_credentials
42
+ end
43
+ end
44
+
45
+ def destroy
46
+ # remove auth instance variables so that after_action does not run
47
+ user = remove_instance_variable(:@resource) if @resource
48
+ client = @token.client
49
+ @token.clear!
50
+
51
+ if user && client && user.tokens[client]
52
+ user.tokens.delete(client)
53
+ user.save!
54
+
55
+ if DeviseTokenAuth.cookie_enabled
56
+ # If a cookie is set with a domain specified then it must be deleted with that domain specified
57
+ # See https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html
58
+ cookies.delete(DeviseTokenAuth.cookie_name, domain: DeviseTokenAuth.cookie_attributes[:domain])
59
+ end
60
+
61
+ yield user if block_given?
62
+
63
+ render_destroy_success
64
+ else
65
+ render_destroy_error
66
+ end
67
+ end
68
+
69
+ protected
70
+
71
+ def valid_params?(key, val)
72
+ resource_params[:password] && key && val
73
+ end
74
+
75
+ def get_auth_params
76
+ auth_key = nil
77
+ auth_val = nil
78
+ # iterate thru allowed auth keys, use first found
79
+ resource_class.authentication_keys.each do |k|
80
+ if resource_params[k]
81
+ auth_val = resource_params[k]
82
+ auth_key = k
83
+ break
84
+ end
85
+ end
86
+
87
+ # honor devise configuration for case_insensitive_keys
88
+ if resource_class.case_insensitive_keys.include?(auth_key)
89
+ auth_val.downcase!
90
+ end
91
+
92
+ { key: auth_key, val: auth_val }
93
+ end
94
+
95
+ def render_new_error
96
+ render_error(405, I18n.t('devise_token_auth.sessions.not_supported'))
97
+ end
98
+
99
+ def render_create_success
100
+ render json: {
101
+ data: resource_data(resource_json: @resource.token_validation_response)
102
+ }
103
+ end
104
+
105
+ def render_create_error_not_confirmed
106
+ render_error(401, I18n.t('devise_token_auth.sessions.not_confirmed', email: @resource.email))
107
+ end
108
+
109
+ def render_create_error_account_locked
110
+ render_error(401, I18n.t('devise.mailer.unlock_instructions.account_lock_msg'))
111
+ end
112
+
113
+ def render_create_error_bad_credentials
114
+ render_error(401, I18n.t('devise_token_auth.sessions.bad_credentials'))
115
+ end
116
+
117
+ def render_destroy_success
118
+ render json: {
119
+ success:true
120
+ }, status: 200
121
+ end
122
+
123
+ def render_destroy_error
124
+ render_error(404, I18n.t('devise_token_auth.sessions.user_not_found'))
125
+ end
126
+
127
+ private
128
+
129
+ def resource_params
130
+ params.permit(*params_for_resource(:sign_in))
131
+ end
132
+
133
+ def create_and_assign_token
134
+ if @resource.respond_to?(:with_lock) && !@resource.changed?
135
+ @resource.with_lock do
136
+ @token = @resource.create_token
137
+ @resource.save!
138
+ end
139
+ else
140
+ @token = @resource.create_token
141
+ @resource.save!
142
+ end
143
+ end
144
+
145
+ def hash_password_in_paranoid_mode
146
+ # In order to avoid timing attacks in paranoid mode, we want the password hash to be
147
+ # calculated even if no resource has been found. Devise's DatabaseAuthenticatable warden
148
+ # strategy handles this case similarly:
149
+ # https://github.com/heartcombo/devise/blob/main/lib/devise/strategies/database_authenticatable.rb
150
+ resource_class.new.password = resource_params[:password] if Devise.paranoid
151
+ end
152
+ end
153
+ end
@@ -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,94 @@
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
+ redirect_options)
49
+ else
50
+ render_show_error
51
+ end
52
+ end
53
+
54
+ private
55
+ def after_unlock_path_for(resource)
56
+ #TODO: This should probably be a configuration option at the very least.
57
+ '/'
58
+ end
59
+
60
+ def render_create_error_missing_email
61
+ render_error(401, I18n.t('devise_token_auth.unlocks.missing_email'))
62
+ end
63
+
64
+ def render_create_success
65
+ render json: {
66
+ success: true,
67
+ message: success_message('unlocks', @email)
68
+ }
69
+ end
70
+
71
+ def render_create_error(errors)
72
+ render json: {
73
+ success: false,
74
+ errors: errors
75
+ }, status: 400
76
+ end
77
+
78
+ def render_show_error
79
+ raise ActionController::RoutingError, 'Not Found'
80
+ end
81
+
82
+ def render_not_found_error
83
+ if Devise.paranoid
84
+ render_create_success
85
+ else
86
+ render_error(404, I18n.t('devise_token_auth.unlocks.user_not_found', email: @email))
87
+ end
88
+ end
89
+
90
+ def resource_params
91
+ params.permit(:email, :unlock_token, :config)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,18 @@
1
+ module DeviseTokenAuth::Concerns::ActiveRecordSupport
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ if Rails.gem_version >= Gem::Version.new("7.1.0.a")
6
+ serialize :tokens, coder: DeviseTokenAuth::Concerns::TokensSerialization
7
+ else
8
+ serialize :tokens, DeviseTokenAuth::Concerns::TokensSerialization
9
+ end
10
+ end
11
+
12
+ class_methods do
13
+ # It's abstract replacement .find_by
14
+ def dta_find_by(attrs = {})
15
+ find_by(attrs)
16
+ end
17
+ end
18
+ 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 `devise_will_save_change_to_email?` 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
@@ -0,0 +1,282 @@
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 devise_will_save_change_to_email?; false; end
45
+
46
+ if DeviseTokenAuth.send_confirmation_email && devise_modules.include?(:confirmable)
47
+ include DeviseTokenAuth::Concerns::ConfirmableSupport
48
+ end
49
+
50
+ def password_required?
51
+ return false unless provider == 'email'
52
+ super
53
+ end
54
+
55
+ # override devise method to include additional info as opts hash
56
+ def send_confirmation_instructions(opts = {})
57
+ generate_confirmation_token! unless @raw_confirmation_token
58
+
59
+ # fall back to "default" config name
60
+ opts[:client_config] ||= 'default'
61
+ opts[:to] = unconfirmed_email if pending_reconfirmation?
62
+ opts[:redirect_url] ||= DeviseTokenAuth.default_confirm_success_url
63
+
64
+ send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
65
+ end
66
+
67
+ # override devise method to include additional info as opts hash
68
+ def send_reset_password_instructions(opts = {})
69
+ token = set_reset_password_token
70
+
71
+ # fall back to "default" config name
72
+ opts[:client_config] ||= 'default'
73
+
74
+ send_devise_notification(:reset_password_instructions, token, opts)
75
+ token
76
+ end
77
+
78
+ # override devise method to include additional info as opts hash
79
+ def send_unlock_instructions(opts = {})
80
+ raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
81
+ self.unlock_token = enc
82
+ save(validate: false)
83
+
84
+ # fall back to "default" config name
85
+ opts[:client_config] ||= 'default'
86
+
87
+ send_devise_notification(:unlock_instructions, raw, opts)
88
+ raw
89
+ end
90
+
91
+ def create_token(client: nil, lifespan: nil, cost: nil, **token_extras)
92
+ token = DeviseTokenAuth::TokenFactory.create(client: client, lifespan: lifespan, cost: cost)
93
+
94
+ tokens[token.client] = {
95
+ token: token.token_hash,
96
+ expiry: token.expiry
97
+ }.merge!(token_extras)
98
+
99
+ clean_old_tokens
100
+
101
+ token
102
+ end
103
+ end
104
+
105
+ def valid_token?(token, client = 'default')
106
+ return false unless tokens[client]
107
+ return true if token_is_current?(token, client)
108
+ return true if token_can_be_reused?(token, client)
109
+
110
+ # return false if none of the above conditions are met
111
+ false
112
+ end
113
+
114
+ # this must be done from the controller so that additional params
115
+ # can be passed on from the client
116
+ def send_confirmation_notification?; false; end
117
+
118
+ def token_is_current?(token, client)
119
+ # ghetto HashWithIndifferentAccess
120
+ expiry = tokens[client]['expiry'] || tokens[client][:expiry]
121
+ token_hash = tokens[client]['token'] || tokens[client][:token]
122
+ previous_token_hash = tokens[client]['previous_token'] || tokens[client][:previous_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
+ (
133
+ # check if the latest token matches
134
+ does_token_match?(token_hash, token) ||
135
+
136
+ # check if the previous token matches
137
+ does_token_match?(previous_token_hash, token)
138
+ )
139
+ )
140
+ end
141
+
142
+ # check if the hash of received token matches the stored token
143
+ def does_token_match?(token_hash, token)
144
+ return false if token_hash.nil?
145
+
146
+ DeviseTokenAuth::Concerns::User.tokens_match?(token_hash, token)
147
+ end
148
+
149
+ # allow batch requests to use the last token
150
+ def token_can_be_reused?(token, client)
151
+ # ghetto HashWithIndifferentAccess
152
+ updated_at = tokens[client]['updated_at'] || tokens[client][:updated_at]
153
+ last_token_hash = tokens[client]['last_token'] || tokens[client][:last_token]
154
+
155
+ return true if (
156
+ # ensure that the last token and its creation time exist
157
+ updated_at && last_token_hash &&
158
+
159
+ # ensure that last token falls within the batch buffer throttle time of the last request
160
+ updated_at.to_time > Time.zone.now - DeviseTokenAuth.batch_request_buffer_throttle &&
161
+
162
+ # ensure that the token is valid
163
+ DeviseTokenAuth::TokenFactory.token_hash_is_token?(last_token_hash, token)
164
+ )
165
+ end
166
+
167
+ # update user's auth token (should happen on each request)
168
+ def create_new_auth_token(client = nil)
169
+ now = Time.zone.now
170
+
171
+ token = create_token(
172
+ client: client,
173
+ previous_token: tokens.fetch(client, {})['token'],
174
+ last_token: tokens.fetch(client, {})['previous_token'],
175
+ updated_at: now
176
+ )
177
+
178
+ update_auth_headers(token.token, token.client)
179
+ end
180
+
181
+ def build_auth_headers(token, client = 'default')
182
+ # client may use expiry to prevent validation request if expired
183
+ # must be cast as string or headers will break
184
+ expiry = tokens[client]['expiry'] || tokens[client][:expiry]
185
+ headers = {
186
+ DeviseTokenAuth.headers_names[:"access-token"] => token,
187
+ DeviseTokenAuth.headers_names[:"token-type"] => 'Bearer',
188
+ DeviseTokenAuth.headers_names[:"client"] => client,
189
+ DeviseTokenAuth.headers_names[:"expiry"] => expiry.to_s,
190
+ DeviseTokenAuth.headers_names[:"uid"] => uid
191
+ }
192
+ headers.merge(build_bearer_token(headers))
193
+ end
194
+
195
+ def build_bearer_token(auth)
196
+ return {} if DeviseTokenAuth.cookie_enabled # There is no need for the bearer token if it is using cookies
197
+
198
+ encoded_token = Base64.strict_encode64(auth.to_json)
199
+ bearer_token = "Bearer #{encoded_token}"
200
+ { DeviseTokenAuth.headers_names[:"authorization"] => bearer_token }
201
+ end
202
+
203
+ def update_auth_headers(token, client = 'default')
204
+ headers = build_auth_headers(token, client)
205
+ clean_old_tokens
206
+ save!
207
+
208
+ headers
209
+ end
210
+
211
+ def build_auth_url(base_url, args)
212
+ args[:uid] = uid
213
+ args[:expiry] = tokens[args[:client_id]]['expiry']
214
+
215
+ DeviseTokenAuth::Url.generate(base_url, args)
216
+ end
217
+
218
+ def extend_batch_buffer(token, client)
219
+ tokens[client]['updated_at'] = Time.zone.now
220
+ update_auth_headers(token, client)
221
+ end
222
+
223
+ def confirmed?
224
+ devise_modules.exclude?(:confirmable) || super
225
+ end
226
+
227
+ def token_validation_response
228
+ as_json(except: %i[tokens created_at updated_at])
229
+ end
230
+
231
+ protected
232
+
233
+ def destroy_expired_tokens
234
+ if tokens
235
+ tokens.delete_if do |cid, v|
236
+ expiry = v[:expiry] || v['expiry']
237
+ DateTime.strptime(expiry.to_s, '%s') < Time.zone.now
238
+ end
239
+ end
240
+ end
241
+
242
+ def should_remove_tokens_after_password_reset?
243
+ DeviseTokenAuth.remove_tokens_after_password_reset &&
244
+ (respond_to?(:encrypted_password_changed?) && encrypted_password_changed?)
245
+ end
246
+
247
+ def remove_tokens_after_password_reset
248
+ return unless should_remove_tokens_after_password_reset?
249
+
250
+ if tokens.present? && tokens.many?
251
+ client, token_data = tokens.max_by { |cid, v| v[:expiry] || v['expiry'] }
252
+ self.tokens = { client => token_data }
253
+ end
254
+ end
255
+
256
+ def max_client_tokens_exceeded?
257
+ tokens.length > DeviseTokenAuth.max_number_of_devices
258
+ end
259
+
260
+ def clean_old_tokens
261
+ return if tokens.blank? || !max_client_tokens_exceeded?
262
+
263
+ # First, remove any tokens with expiry greater than current max allowed lifespan
264
+ # this handles the case where token lifespan was reduced and old tokens exist
265
+ max_lifespan_expiry = Time.now.to_i + DeviseTokenAuth.token_lifespan.to_i
266
+ tokens_to_keep = tokens.select do |_cid, v|
267
+ expiry = (v[:expiry] || v['expiry']).to_i
268
+ expiry <= max_lifespan_expiry
269
+ end
270
+
271
+ # Using Enumerable#sort_by on a Hash will typecast it into an associative
272
+ # Array (i.e. an Array of key-value Array pairs). However, since Hashes
273
+ # have an internal order in Ruby 1.9+, the resulting sorted associative
274
+ # Array can be converted back into a Hash, while maintaining the sorted
275
+ # order.
276
+ self.tokens = tokens_to_keep.sort_by { |_cid, v| v[:expiry] || v['expiry'] }.to_h
277
+
278
+ # Since the tokens are sorted by expiry, shift the oldest client token
279
+ # off the Hash until it no longer exceeds the maximum number of clients
280
+ tokens.shift while max_client_tokens_exceeded?
281
+ end
282
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeviseTokenAuth::Concerns::UserOmniauthCallbacks
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ validates :email, presence: true, if: lambda { uid_and_provider_defined? && email_provider? }
8
+ validates :email, :devise_token_auth_email => true, allow_nil: true, allow_blank: true, if: lambda { uid_and_provider_defined? && email_provider? }
9
+ validates_presence_of :uid, if: lambda { uid_and_provider_defined? && !email_provider? }
10
+
11
+ # Provide support for devise-multi_email - they implement case_sensitive
12
+ # and maintain uniqueness themselves.
13
+ unless Gem.loaded_specs[ 'devise-multi_email' ]
14
+ # only validate unique emails among email registration users
15
+ validates :email, uniqueness: { case_sensitive: false, scope: :provider }, on: :create, if: lambda { uid_and_provider_defined? && email_provider? }
16
+ end
17
+
18
+ # keep uid in sync with email
19
+ before_save :sync_uid
20
+ before_create :sync_uid
21
+ end
22
+
23
+ protected
24
+
25
+ def uid_and_provider_defined?
26
+ defined?(provider) && defined?(uid)
27
+ end
28
+
29
+ def email_provider?
30
+ provider == 'email'
31
+ end
32
+
33
+ def sync_uid
34
+ unless self.new_record?
35
+ return if devise_modules.include?(:confirmable) && !@bypass_confirmation_postpone && postpone_email_change?
36
+ end
37
+ self.uid = email if uid_and_provider_defined? && email_provider?
38
+ end
39
+ end