digix_devise_token_auth 0.1.44

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 (149) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +13 -0
  3. data/README.md +952 -0
  4. data/Rakefile +35 -0
  5. data/app/controllers/devise_token_auth/application_controller.rb +76 -0
  6. data/app/controllers/devise_token_auth/concerns/resource_finder.rb +43 -0
  7. data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +165 -0
  8. data/app/controllers/devise_token_auth/confirmations_controller.rb +30 -0
  9. data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +243 -0
  10. data/app/controllers/devise_token_auth/passwords_controller.rb +202 -0
  11. data/app/controllers/devise_token_auth/registrations_controller.rb +205 -0
  12. data/app/controllers/devise_token_auth/sessions_controller.rb +133 -0
  13. data/app/controllers/devise_token_auth/token_validations_controller.rb +29 -0
  14. data/app/controllers/devise_token_auth/unlocks_controller.rb +89 -0
  15. data/app/models/devise_token_auth/concerns/user.rb +260 -0
  16. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +26 -0
  17. data/app/validators/email_validator.rb +21 -0
  18. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  19. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  20. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  21. data/app/views/devise_token_auth/omniauth_external_window.html.erb +38 -0
  22. data/config/initializers/devise.rb +196 -0
  23. data/config/locales/da-DK.yml +50 -0
  24. data/config/locales/de.yml +49 -0
  25. data/config/locales/en.yml +50 -0
  26. data/config/locales/es.yml +49 -0
  27. data/config/locales/fr.yml +49 -0
  28. data/config/locales/it.yml +46 -0
  29. data/config/locales/ja.yml +46 -0
  30. data/config/locales/nl.yml +30 -0
  31. data/config/locales/pl.yml +48 -0
  32. data/config/locales/pt-BR.yml +46 -0
  33. data/config/locales/pt.yml +48 -0
  34. data/config/locales/ro.yml +46 -0
  35. data/config/locales/ru.yml +50 -0
  36. data/config/locales/sq.yml +46 -0
  37. data/config/locales/uk.yml +59 -0
  38. data/config/locales/vi.yml +50 -0
  39. data/config/locales/zh-CN.yml +46 -0
  40. data/config/locales/zh-HK.yml +48 -0
  41. data/config/locales/zh-TW.yml +48 -0
  42. data/lib/devise_token_auth.rb +8 -0
  43. data/lib/devise_token_auth/controllers/helpers.rb +149 -0
  44. data/lib/devise_token_auth/controllers/url_helpers.rb +8 -0
  45. data/lib/devise_token_auth/engine.rb +90 -0
  46. data/lib/devise_token_auth/rails/routes.rb +114 -0
  47. data/lib/devise_token_auth/url.rb +37 -0
  48. data/lib/devise_token_auth/version.rb +3 -0
  49. data/lib/generators/devise_token_auth/USAGE +31 -0
  50. data/lib/generators/devise_token_auth/install_generator.rb +160 -0
  51. data/lib/generators/devise_token_auth/install_views_generator.rb +16 -0
  52. data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +48 -0
  53. data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +55 -0
  54. data/lib/generators/devise_token_auth/templates/user.rb +7 -0
  55. data/lib/tasks/devise_token_auth_tasks.rake +4 -0
  56. data/test/controllers/custom/custom_confirmations_controller_test.rb +21 -0
  57. data/test/controllers/custom/custom_omniauth_callbacks_controller_test.rb +29 -0
  58. data/test/controllers/custom/custom_passwords_controller_test.rb +75 -0
  59. data/test/controllers/custom/custom_registrations_controller_test.rb +54 -0
  60. data/test/controllers/custom/custom_sessions_controller_test.rb +37 -0
  61. data/test/controllers/custom/custom_token_validations_controller_test.rb +40 -0
  62. data/test/controllers/demo_group_controller_test.rb +153 -0
  63. data/test/controllers/demo_mang_controller_test.rb +284 -0
  64. data/test/controllers/demo_user_controller_test.rb +601 -0
  65. data/test/controllers/devise_token_auth/confirmations_controller_test.rb +129 -0
  66. data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +371 -0
  67. data/test/controllers/devise_token_auth/passwords_controller_test.rb +649 -0
  68. data/test/controllers/devise_token_auth/registrations_controller_test.rb +878 -0
  69. data/test/controllers/devise_token_auth/sessions_controller_test.rb +500 -0
  70. data/test/controllers/devise_token_auth/token_validations_controller_test.rb +90 -0
  71. data/test/controllers/devise_token_auth/unlocks_controller_test.rb +194 -0
  72. data/test/controllers/overrides/confirmations_controller_test.rb +43 -0
  73. data/test/controllers/overrides/omniauth_callbacks_controller_test.rb +49 -0
  74. data/test/controllers/overrides/passwords_controller_test.rb +66 -0
  75. data/test/controllers/overrides/registrations_controller_test.rb +40 -0
  76. data/test/controllers/overrides/sessions_controller_test.rb +33 -0
  77. data/test/controllers/overrides/token_validations_controller_test.rb +41 -0
  78. data/test/dummy/README.rdoc +28 -0
  79. data/test/dummy/app/controllers/application_controller.rb +16 -0
  80. data/test/dummy/app/controllers/auth_origin_controller.rb +5 -0
  81. data/test/dummy/app/controllers/custom/confirmations_controller.rb +13 -0
  82. data/test/dummy/app/controllers/custom/omniauth_callbacks_controller.rb +11 -0
  83. data/test/dummy/app/controllers/custom/passwords_controller.rb +40 -0
  84. data/test/dummy/app/controllers/custom/registrations_controller.rb +39 -0
  85. data/test/dummy/app/controllers/custom/sessions_controller.rb +29 -0
  86. data/test/dummy/app/controllers/custom/token_validations_controller.rb +19 -0
  87. data/test/dummy/app/controllers/demo_group_controller.rb +13 -0
  88. data/test/dummy/app/controllers/demo_mang_controller.rb +12 -0
  89. data/test/dummy/app/controllers/demo_user_controller.rb +25 -0
  90. data/test/dummy/app/controllers/overrides/confirmations_controller.rb +26 -0
  91. data/test/dummy/app/controllers/overrides/omniauth_callbacks_controller.rb +14 -0
  92. data/test/dummy/app/controllers/overrides/passwords_controller.rb +33 -0
  93. data/test/dummy/app/controllers/overrides/registrations_controller.rb +27 -0
  94. data/test/dummy/app/controllers/overrides/sessions_controller.rb +36 -0
  95. data/test/dummy/app/controllers/overrides/token_validations_controller.rb +23 -0
  96. data/test/dummy/app/helpers/application_helper.rb +1065 -0
  97. data/test/dummy/app/models/evil_user.rb +3 -0
  98. data/test/dummy/app/models/lockable_user.rb +5 -0
  99. data/test/dummy/app/models/mang.rb +3 -0
  100. data/test/dummy/app/models/nice_user.rb +7 -0
  101. data/test/dummy/app/models/only_email_user.rb +5 -0
  102. data/test/dummy/app/models/scoped_user.rb +7 -0
  103. data/test/dummy/app/models/unconfirmable_user.rb +8 -0
  104. data/test/dummy/app/models/unregisterable_user.rb +7 -0
  105. data/test/dummy/app/models/user.rb +18 -0
  106. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  107. data/test/dummy/config.ru +16 -0
  108. data/test/dummy/config/application.rb +24 -0
  109. data/test/dummy/config/application.yml.bk +0 -0
  110. data/test/dummy/config/boot.rb +5 -0
  111. data/test/dummy/config/environment.rb +5 -0
  112. data/test/dummy/config/environments/development.rb +44 -0
  113. data/test/dummy/config/environments/production.rb +82 -0
  114. data/test/dummy/config/environments/test.rb +48 -0
  115. data/test/dummy/config/initializers/assets.rb +8 -0
  116. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  117. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  118. data/test/dummy/config/initializers/devise.rb +3 -0
  119. data/test/dummy/config/initializers/devise_token_auth.rb +22 -0
  120. data/test/dummy/config/initializers/figaro.rb +1 -0
  121. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  122. data/test/dummy/config/initializers/inflections.rb +16 -0
  123. data/test/dummy/config/initializers/mime_types.rb +4 -0
  124. data/test/dummy/config/initializers/omniauth.rb +8 -0
  125. data/test/dummy/config/initializers/session_store.rb +3 -0
  126. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  127. data/test/dummy/config/routes.rb +72 -0
  128. data/test/dummy/config/spring.rb +1 -0
  129. data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +63 -0
  130. data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +62 -0
  131. data/test/dummy/db/migrate/20140829044006_add_operating_thetan_to_user.rb +6 -0
  132. data/test/dummy/db/migrate/20140916224624_add_favorite_color_to_mangs.rb +5 -0
  133. data/test/dummy/db/migrate/20140928231203_devise_token_auth_create_evil_users.rb +64 -0
  134. data/test/dummy/db/migrate/20141222035835_devise_token_auth_create_only_email_users.rb +60 -0
  135. data/test/dummy/db/migrate/20141222053502_devise_token_auth_create_unregisterable_users.rb +61 -0
  136. data/test/dummy/db/migrate/20150409095712_devise_token_auth_create_nice_users.rb +61 -0
  137. data/test/dummy/db/migrate/20150708104536_devise_token_auth_create_unconfirmable_users.rb +61 -0
  138. data/test/dummy/db/migrate/20160103235141_devise_token_auth_create_scoped_users.rb +61 -0
  139. data/test/dummy/db/migrate/20160629184441_devise_token_auth_create_lockable_users.rb +61 -0
  140. data/test/dummy/db/schema.rb +258 -0
  141. data/test/dummy/lib/migration_database_helper.rb +29 -0
  142. data/test/integration/navigation_test.rb +10 -0
  143. data/test/lib/devise_token_auth/url_test.rb +24 -0
  144. data/test/lib/generators/devise_token_auth/install_generator_test.rb +187 -0
  145. data/test/lib/generators/devise_token_auth/install_views_generator_test.rb +23 -0
  146. data/test/models/only_email_user_test.rb +35 -0
  147. data/test/models/user_test.rb +169 -0
  148. data/test/test_helper.rb +77 -0
  149. metadata +342 -0
@@ -0,0 +1,29 @@
1
+ module DeviseTokenAuth
2
+ class TokenValidationsController < DeviseTokenAuth::ApplicationController
3
+ skip_before_action :assert_is_devise_resource!, :only => [:validate_token]
4
+ before_action :set_user_by_token, :only => [:validate_token]
5
+
6
+ def validate_token
7
+ # @resource will have been set by set_user_by_token concern
8
+ if @resource
9
+ yield @resource if block_given?
10
+ render_validate_token_success
11
+ else
12
+ render_validate_token_error
13
+ end
14
+ end
15
+
16
+ protected
17
+
18
+ def render_validate_token_success
19
+ render json: {
20
+ success: true,
21
+ data: resource_data(resource_json: @resource.token_validation_response)
22
+ }
23
+ end
24
+
25
+ def render_validate_token_error
26
+ render_error(401, I18n.t("devise_token_auth.token_validations.invalid"))
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,89 @@
1
+ module DeviseTokenAuth
2
+ class UnlocksController < DeviseTokenAuth::ApplicationController
3
+ skip_after_action :update_auth_header, :only => [:create, :show]
4
+
5
+ # this action is responsible for generating unlock tokens and
6
+ # sending emails
7
+ def create
8
+ unless resource_params[:email]
9
+ return render_create_error_missing_email
10
+ end
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 && @resource.id
38
+ client_id, 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,
44
+ client_id,
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.new('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,260 @@
1
+ require 'bcrypt'
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] ||= (::BCrypt::Password.new(token_hash) == token)
11
+ if @token_equality_cache.size > 10000
12
+ @token_equality_cache = {}
13
+ end
14
+ result
15
+ end
16
+
17
+ included do
18
+ # Hack to check if devise is already enabled
19
+ unless self.method_defined?(:devise_modules)
20
+ devise :database_authenticatable, :registerable,
21
+ :recoverable, :trackable, :validatable, :confirmable
22
+ else
23
+ self.devise_modules.delete(:omniauthable)
24
+ end
25
+
26
+ unless tokens_has_json_column_type?
27
+ serialize :tokens, JSON
28
+ end
29
+
30
+ if DeviseTokenAuth.default_callbacks
31
+ include DeviseTokenAuth::Concerns::UserOmniauthCallbacks
32
+ end
33
+
34
+ # can't set default on text fields in mysql, simulate here instead.
35
+ after_save :set_empty_token_hash
36
+ after_initialize :set_empty_token_hash
37
+
38
+ # get rid of dead tokens
39
+ before_save :destroy_expired_tokens
40
+
41
+ # remove old tokens if password has changed
42
+ before_save :remove_tokens_after_password_reset
43
+
44
+ # don't use default devise email validation
45
+ def email_required?; false; end
46
+ def email_changed?; false; end
47
+ def will_save_change_to_email?; false; end
48
+
49
+ def password_required?
50
+ return false unless provider == 'email'
51
+ super
52
+ end
53
+
54
+ # override devise method to include additional info as opts hash
55
+ def send_confirmation_instructions(opts={})
56
+ generate_confirmation_token! unless @raw_confirmation_token
57
+
58
+ # fall back to "default" config name
59
+ opts[:client_config] ||= "default"
60
+ opts[:to] = unconfirmed_email if pending_reconfirmation?
61
+ opts[:redirect_url] ||= DeviseTokenAuth.default_confirm_success_url
62
+
63
+ send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
64
+ end
65
+
66
+ # override devise method to include additional info as opts hash
67
+ def send_reset_password_instructions(opts={})
68
+ token = set_reset_password_token
69
+
70
+ # fall back to "default" config name
71
+ opts[:client_config] ||= "default"
72
+
73
+ send_devise_notification(:reset_password_instructions, token, opts)
74
+ token
75
+ end
76
+
77
+ # override devise method to include additional info as opts hash
78
+ def send_unlock_instructions(opts={})
79
+ raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
80
+ self.unlock_token = enc
81
+ save(validate: false)
82
+
83
+ # fall back to "default" config name
84
+ opts[:client_config] ||= "default"
85
+
86
+ send_devise_notification(:unlock_instructions, raw, opts)
87
+ raw
88
+ end
89
+ end
90
+
91
+ def create_token(client_id: nil, token: nil, expiry: nil, **token_extras)
92
+ client_id ||= SecureRandom.urlsafe_base64(nil, false)
93
+ token ||= SecureRandom.urlsafe_base64(nil, false)
94
+ expiry ||= (Time.now + token_lifespan).to_i
95
+
96
+ self.tokens[client_id] = {
97
+ token: BCrypt::Password.create(token),
98
+ expiry: expiry
99
+ }.merge!(token_extras)
100
+
101
+ [client_id, token, expiry]
102
+ end
103
+
104
+ module ClassMethods
105
+ protected
106
+
107
+ def tokens_has_json_column_type?
108
+ database_exists? && table_exists? && self.columns_hash['tokens'] && self.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
113
+ end
114
+ end
115
+
116
+
117
+ def valid_token?(token, client_id='default')
118
+ return false unless tokens[client_id]
119
+ return true if token_is_current?(token, client_id)
120
+ return true if token_can_be_reused?(token, client_id)
121
+
122
+ # return false if none of the above conditions are met
123
+ return false
124
+ end
125
+
126
+
127
+ # this must be done from the controller so that additional params
128
+ # can be passed on from the client
129
+ def send_confirmation_notification?; false; end
130
+
131
+
132
+ def token_is_current?(token, client_id)
133
+ # ghetto HashWithIndifferentAccess
134
+ expiry = tokens[client_id]['expiry'] || tokens[client_id][:expiry]
135
+ token_hash = tokens[client_id]['token'] || tokens[client_id][:token]
136
+
137
+ return true if (
138
+ # ensure that expiry and token are set
139
+ expiry && token &&
140
+
141
+ # ensure that the token has not yet expired
142
+ DateTime.strptime(expiry.to_s, '%s') > Time.now &&
143
+
144
+ # ensure that the token is valid
145
+ DeviseTokenAuth::Concerns::User.tokens_match?(token_hash, token)
146
+ )
147
+ end
148
+
149
+
150
+ # allow batch requests to use the previous token
151
+ def token_can_be_reused?(token, client_id)
152
+ # ghetto HashWithIndifferentAccess
153
+ updated_at = tokens[client_id]['updated_at'] || tokens[client_id][:updated_at]
154
+ last_token = tokens[client_id]['last_token'] || tokens[client_id][:last_token]
155
+
156
+ return true if (
157
+ # ensure that the last token and its creation time exist
158
+ updated_at && last_token &&
159
+
160
+ # ensure that previous token falls within the batch buffer throttle time of the last request
161
+ Time.parse(updated_at) > Time.now - DeviseTokenAuth.batch_request_buffer_throttle &&
162
+
163
+ # ensure that the token is valid
164
+ ::BCrypt::Password.new(last_token) == token
165
+ )
166
+ end
167
+
168
+
169
+ # update user's auth token (should happen on each request)
170
+ def create_new_auth_token(client_id=nil)
171
+ now = Time.now
172
+
173
+ client_id, token = create_token(
174
+ client_id: client_id,
175
+ expiry: (now + token_lifespan).to_i,
176
+ last_token: tokens.fetch(client_id, {})['token'],
177
+ updated_at: now
178
+ )
179
+
180
+ update_auth_header(token, client_id)
181
+ end
182
+
183
+ def build_auth_header(token, client_id='default')
184
+ # client may use expiry to prevent validation request if expired
185
+ # must be cast as string or headers will break
186
+ expiry = tokens[client_id]['expiry'] || tokens[client_id][:expiry]
187
+
188
+ {
189
+ DeviseTokenAuth.headers_names[:"access-token"] => token,
190
+ DeviseTokenAuth.headers_names[:"token-type"] => "Bearer",
191
+ DeviseTokenAuth.headers_names[:"client"] => client_id,
192
+ DeviseTokenAuth.headers_names[:"expiry"] => expiry.to_s,
193
+ DeviseTokenAuth.headers_names[:"uid"] => uid
194
+ }
195
+ end
196
+
197
+ def update_auth_header(token, client_id='default')
198
+ headers = build_auth_header(token, client_id)
199
+ while tokens.length > 0 && DeviseTokenAuth.max_number_of_devices < tokens.length
200
+ oldest_client_id, _tk = tokens.min_by { |_cid, v| v[:expiry] || v["expiry"] }
201
+ tokens.delete(oldest_client_id)
202
+ end
203
+
204
+ save!
205
+
206
+ headers
207
+ end
208
+
209
+
210
+ def build_auth_url(base_url, args)
211
+ args[:uid] = uid
212
+ args[:expiry] = tokens[args[:client_id]]['expiry']
213
+
214
+ DeviseTokenAuth::Url.generate(base_url, args)
215
+ end
216
+
217
+
218
+ def extend_batch_buffer(token, client_id)
219
+ self.tokens[client_id]['updated_at'] = Time.now
220
+ update_auth_header(token, client_id)
221
+ end
222
+
223
+ def confirmed?
224
+ devise_modules.exclude?(:confirmable) || super
225
+ end
226
+
227
+ def token_validation_response
228
+ as_json(except: [:tokens, :created_at, :updated_at])
229
+ end
230
+
231
+ def token_lifespan
232
+ DeviseTokenAuth.token_lifespan
233
+ end
234
+
235
+ protected
236
+
237
+ def set_empty_token_hash
238
+ self.tokens ||= {} if has_attribute?(:tokens)
239
+ end
240
+
241
+ def destroy_expired_tokens
242
+ if tokens
243
+ tokens.delete_if do |cid, v|
244
+ expiry = v[:expiry] || v["expiry"]
245
+ DateTime.strptime(expiry.to_s, '%s') < Time.now
246
+ end
247
+ end
248
+ end
249
+
250
+ def remove_tokens_after_password_reset
251
+ should_remove_old_tokens = DeviseTokenAuth.remove_tokens_after_password_reset &&
252
+ encrypted_password_changed? && tokens && tokens.many?
253
+
254
+ if should_remove_old_tokens
255
+ client_id, token_data = tokens.max_by { |cid, v| v[:expiry] || v["expiry"] }
256
+ self.tokens = {client_id => token_data}
257
+ end
258
+ end
259
+
260
+ end
@@ -0,0 +1,26 @@
1
+ module DeviseTokenAuth::Concerns::UserOmniauthCallbacks
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ validates :email, presence: true,if: :email_provider?
6
+ validates :email, email: true, allow_nil: true, allow_blank: true, if: :email_provider?
7
+ validates_presence_of :uid, unless: :email_provider?
8
+
9
+ # only validate unique emails among email registration users
10
+ validates :email, uniqueness: { scope: :provider }, on: :create, if: :email_provider?
11
+
12
+ # keep uid in sync with email
13
+ before_save :sync_uid
14
+ before_create :sync_uid
15
+ end
16
+
17
+ protected
18
+
19
+ def email_provider?
20
+ provider == 'email'
21
+ end
22
+
23
+ def sync_uid
24
+ self.uid = email if provider == 'email'
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ class EmailValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
4
+ record.errors[attribute] << email_invalid_message
5
+ end
6
+ end
7
+
8
+ private
9
+
10
+ def email_invalid_message
11
+ # Try strictly set message:
12
+ message = options[:message]
13
+
14
+ if message.nil?
15
+ # Try DeviceTokenAuth translations or fallback to ActiveModel translations
16
+ message = I18n.t(:'errors.messages.not_email', default: :'errors.messages.invalid')
17
+ end
18
+
19
+ message
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ <p><%= t(:welcome).capitalize + ' ' + @email %>!</p>
2
+
3
+ <p><%= t '.confirm_link_msg' %> </p>
4
+
5
+ <p><%= link_to t('.confirm_account_link'), confirmation_url(@resource, {confirmation_token: @token, config: message['client-config'].to_s, redirect_url: message['redirect-url']}).html_safe %></p>
@@ -0,0 +1,8 @@
1
+ <p><%= t(:hello).capitalize %> <%= @resource.email %>!</p>
2
+
3
+ <p><%= t '.request_reset_link_msg' %></p>
4
+
5
+ <p><%= link_to t('.password_change_link'), edit_password_url(@resource, reset_password_token: @token, config: message['client-config'].to_s, redirect_url: message['redirect-url'].to_s).html_safe %></p>
6
+
7
+ <p><%= t '.ignore_mail_msg' %></p>
8
+ <p><%= t '.no_changes_msg' %></p>
@@ -0,0 +1,7 @@
1
+ <p><%= t :hello %> <%= @resource.email %>!</p>
2
+
3
+ <p><%= t '.account_lock_msg' %></p>
4
+
5
+ <p><%= t '.unlock_link_msg' %></p>
6
+
7
+ <p><%= link_to t('.unlock_link'), unlock_url(@resource, unlock_token: @token, config: message['client-config'].to_s) %></p>