devise_token_auth_multi_email 0.9.4 → 0.9.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/Rakefile +4 -8
  4. data/app/controllers/devise_token_auth/concerns/resource_finder.rb +2 -9
  5. data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +35 -5
  6. data/app/controllers/devise_token_auth/registrations_controller.rb +24 -18
  7. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +6 -3
  8. data/lib/devise_token_auth/engine.rb +40 -1
  9. data/lib/devise_token_auth/version.rb +1 -1
  10. data/test/controllers/devise_token_auth/confirmations_controller_test.rb +4 -4
  11. data/test/controllers/devise_token_auth/multi_email_coexistence_test.rb +130 -0
  12. data/test/controllers/devise_token_auth/multi_email_confirmations_controller_test.rb +210 -0
  13. data/test/controllers/devise_token_auth/multi_email_passwords_controller_test.rb +247 -0
  14. data/test/controllers/devise_token_auth/multi_email_registrations_controller_test.rb +137 -0
  15. data/test/controllers/devise_token_auth/multi_email_sessions_controller_test.rb +191 -0
  16. data/test/controllers/devise_token_auth/multi_email_token_validations_controller_test.rb +140 -0
  17. data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +5 -4
  18. data/test/controllers/devise_token_auth/standard_user_registrations_controller_test.rb +165 -0
  19. data/test/coverage/assets/0.13.2/colorbox/loading.gif +0 -0
  20. data/test/coverage/assets/0.13.2/loading.gif +0 -0
  21. data/test/dummy/app/active_record/multi_email_user.rb +45 -0
  22. data/test/dummy/app/active_record/multi_email_user_email.rb +21 -0
  23. data/test/dummy/config/application.rb +6 -1
  24. data/test/dummy/config/initializers/omniauth.rb +15 -1
  25. data/test/dummy/config/routes.rb +8 -0
  26. data/test/dummy/db/migrate/20260401000001_devise_token_auth_create_multi_email_users.rb +49 -0
  27. data/test/dummy/db/migrate/20260401000002_devise_token_auth_create_multi_email_user_emails.rb +29 -0
  28. data/test/dummy/db/schema.rb +81 -41
  29. data/test/dummy/db/test.sqlite3-shm +0 -0
  30. data/test/dummy/tmp/generators/app/controllers/application_controller.rb +6 -0
  31. data/test/dummy/tmp/generators/app/models/{user.rb → azpire/v1/human_resource/user.rb} +1 -1
  32. data/test/dummy/tmp/generators/config/initializers/devise_token_auth.rb +11 -5
  33. data/test/dummy/tmp/generators/db/migrate/{20210305040222_devise_token_auth_create_users.rb → 20260408021432_devise_token_auth_create_azpire_v1_human_resource_users.rb} +7 -7
  34. data/test/factories/users.rb +1 -0
  35. data/test/lib/devise_token_auth/controllers/helpers_test.rb +402 -0
  36. data/test/lib/devise_token_auth/token_factory_test.rb +18 -18
  37. data/test/lib/generators/devise_token_auth/install_generator_test.rb +60 -0
  38. data/test/lib/generators/devise_token_auth/install_generator_with_namespace_test.rb +1 -1
  39. data/test/lib/generators/devise_token_auth/install_mongoid_generator_test.rb +218 -0
  40. data/test/models/multi_email_user_email_test.rb +95 -0
  41. data/test/models/multi_email_user_test.rb +225 -0
  42. data/test/test_helper.rb +21 -11
  43. data/test/validators/devise_token_auth_email_validator_test.rb +114 -0
  44. metadata +58 -27
  45. data/test/dummy/tmp/generators/app/models/mang.rb +0 -9
  46. data/test/dummy/tmp/generators/config/routes.rb +0 -9
  47. data/test/dummy/tmp/generators/db/migrate/20210305040222_devise_token_auth_create_mangs.rb +0 -49
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ # Tests that verify the TokenValidationsController works correctly with a
6
+ # MultiEmailUser — a model that uses :multi_email_authenticatable from the
7
+ # devise-multi_email gem.
8
+ #
9
+ # Token validation uses the uid / access-token / client headers; the uid is the
10
+ # primary email address, which is synced to the uid column by the sync_uid
11
+ # before_save callback. No multi-email-specific lookup is needed here because
12
+ # the token is looked up by uid (a real column), so this test primarily
13
+ # confirms that the standard flow works end-to-end for multi-email users.
14
+ #
15
+ # These tests are ActiveRecord-only — the MultiEmailUser model and its route
16
+ # are not available in Mongoid runs.
17
+ return unless DEVISE_TOKEN_AUTH_ORM == :active_record
18
+
19
+ class MultiEmailTokenValidationsControllerTest < ActionDispatch::IntegrationTest
20
+ describe 'MultiEmailUser token validation' do
21
+ def registration_params(email: nil)
22
+ {
23
+ email: email || Faker::Internet.unique.email,
24
+ password: 'secret123',
25
+ password_confirmation: 'secret123',
26
+ confirm_success_url: Faker::Internet.url
27
+ }
28
+ end
29
+
30
+ # Register a user, confirm it, and sign in. Returns [user, auth_headers].
31
+ def sign_in_confirmed_user
32
+ email = Faker::Internet.unique.email
33
+
34
+ post '/multi_email_auth', params: registration_params(email: email)
35
+ assert_equal 200, response.status, "Registration failed: #{response.body}"
36
+ user = assigns(:resource)
37
+ user.confirm
38
+
39
+ post '/multi_email_auth/sign_in',
40
+ params: { email: email, password: 'secret123' }
41
+ assert_equal 200, response.status, "Sign-in failed: #{response.body}"
42
+
43
+ auth_headers = {
44
+ 'access-token' => response.headers['access-token'],
45
+ 'client' => response.headers['client'],
46
+ 'uid' => response.headers['uid'],
47
+ 'token-type' => response.headers['token-type']
48
+ }
49
+ [user, auth_headers]
50
+ end
51
+
52
+ before do
53
+ @user, @auth_headers = sign_in_confirmed_user
54
+ @client_id = @auth_headers['client']
55
+
56
+ # Age the token so the request is not treated as a batch request.
57
+ age_token(@user, @client_id)
58
+ end
59
+
60
+ # -----------------------------------------------------------------------
61
+ # Valid token
62
+ # -----------------------------------------------------------------------
63
+ describe 'valid token' do
64
+ before do
65
+ get '/multi_email_auth/validate_token', headers: @auth_headers
66
+ @data = JSON.parse(response.body)
67
+ end
68
+
69
+ test 'request is successful' do
70
+ assert_equal 200, response.status
71
+ end
72
+
73
+ test 'response includes user data' do
74
+ assert @data['data']
75
+ end
76
+
77
+ test 'response data includes the correct email' do
78
+ assert @data['data']['email']
79
+ end
80
+ end
81
+
82
+ # -----------------------------------------------------------------------
83
+ # Invalid access-token
84
+ # -----------------------------------------------------------------------
85
+ describe 'invalid access-token' do
86
+ before do
87
+ bad_headers = @auth_headers.merge('access-token' => 'this-is-wrong')
88
+ get '/multi_email_auth/validate_token', headers: bad_headers
89
+ @data = JSON.parse(response.body)
90
+ end
91
+
92
+ test 'request fails' do
93
+ assert_equal 401, response.status
94
+ end
95
+
96
+ test 'response contains errors' do
97
+ assert @data['errors']
98
+ assert_equal [I18n.t('devise_token_auth.token_validations.invalid')],
99
+ @data['errors']
100
+ end
101
+ end
102
+
103
+ # -----------------------------------------------------------------------
104
+ # Missing auth headers
105
+ # -----------------------------------------------------------------------
106
+ describe 'missing auth headers' do
107
+ before do
108
+ get '/multi_email_auth/validate_token'
109
+ @data = JSON.parse(response.body)
110
+ end
111
+
112
+ test 'request fails' do
113
+ assert_equal 401, response.status
114
+ end
115
+
116
+ test 'response contains errors' do
117
+ assert @data['errors']
118
+ end
119
+ end
120
+
121
+ # -----------------------------------------------------------------------
122
+ # Expired token
123
+ # -----------------------------------------------------------------------
124
+ describe 'expired token' do
125
+ before do
126
+ expire_token(@user, @client_id)
127
+ get '/multi_email_auth/validate_token', headers: @auth_headers
128
+ @data = JSON.parse(response.body)
129
+ end
130
+
131
+ test 'request fails' do
132
+ assert_equal 401, response.status
133
+ end
134
+
135
+ test 'response contains errors' do
136
+ assert @data['errors']
137
+ end
138
+ end
139
+ end
140
+ end
@@ -16,9 +16,11 @@ class OmniauthTest < ActionDispatch::IntegrationTest
16
16
  @redirect_url = 'https://ng-token-auth.dev/'
17
17
  end
18
18
 
19
+ # Suggested by GPT-5.2 as a more tolerant alternative to the above regex, which was brittle and failed when the JSON data contained certain characters.
19
20
  def get_parsed_data_json
20
- encoded_json_data = @response.body.match(/var data \= JSON.parse\(decodeURIComponent\(\'(.+)\'\)\)\;/)[1]
21
- JSON.parse(CGI.unescape(encoded_json_data))
21
+ m = response.body.match(/var\s+data\s*=\s*JSON\.parse\(decodeURIComponent\(['"](.+?)['"]\)\)/m)
22
+ raise "Could not find encoded data payload in response body" unless m
23
+ JSON.parse(CGI.unescape(m[1]))
22
24
  end
23
25
 
24
26
  describe 'success callback' do
@@ -79,8 +81,7 @@ class OmniauthTest < ActionDispatch::IntegrationTest
79
81
 
80
82
  test 'should be redirected via valid url' do
81
83
  get_success
82
- assert_equal 'http://www.example.com/auth/facebook/callback',
83
- request.original_url
84
+ assert_equal '/auth/facebook/callback', URI.parse(request.original_url).path
84
85
  end
85
86
 
86
87
  describe 'with default user model' do
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ # Tests that verify devise_token_auth works correctly with a **standard** model —
6
+ # one that does NOT use devise-multi_email (i.e. does NOT have multi_email_association).
7
+ #
8
+ # Standard models get the email uniqueness validation directly from
9
+ # DeviseTokenAuth::Concerns::UserOmniauthCallbacks, regardless of whether the
10
+ # devise-multi_email gem is loaded. These tests confirm:
11
+ # • Basic registration, authentication, and account management work.
12
+ # • Duplicate email registrations are rejected at the model level (no DB
13
+ # constraint exception reaching PostgreSQL that would abort the transaction).
14
+ # • An OAuth user's email can be re-used for an email-provider registration
15
+ # because uniqueness is scoped to provider.
16
+ #
17
+ # These tests are ActiveRecord-specific because they validate AR-level validators
18
+ # and the MultiEmailUser ActiveRecord model (used for contrast tests).
19
+ return unless DEVISE_TOKEN_AUTH_ORM == :active_record
20
+
21
+ class StandardUserRegistrationsControllerTest < ActionDispatch::IntegrationTest
22
+ describe 'Standard User (without multi_email_authenticatable)' do
23
+ def registration_params(email: nil)
24
+ {
25
+ email: email || Faker::Internet.unique.email,
26
+ password: 'secret123',
27
+ password_confirmation: 'secret123',
28
+ confirm_success_url: Faker::Internet.url
29
+ }
30
+ end
31
+
32
+ # -----------------------------------------------------------------------
33
+ # Uniqueness validation lives in the concern for standard models
34
+ # -----------------------------------------------------------------------
35
+ describe 'email uniqueness' do
36
+ test 'validates uniqueness at the model level — not at the DB' do
37
+ # The concern (UserOmniauthCallbacks) adds this validation for standard
38
+ # models so the check happens before the INSERT, keeping PostgreSQL
39
+ # transactions clean.
40
+ assert User.validators_on(:email).any? { |v|
41
+ v.is_a?(ActiveRecord::Validations::UniquenessValidator)
42
+ }, 'Expected a UniquenessValidator on User#email'
43
+
44
+ assert Mang.validators_on(:email).any? { |v|
45
+ v.is_a?(ActiveRecord::Validations::UniquenessValidator)
46
+ }, 'Expected a UniquenessValidator on Mang#email'
47
+ end
48
+
49
+ test 'multi_email model does NOT carry the concern uniqueness validator' do
50
+ # MultiEmailUser uses :multi_email_authenticatable so it has
51
+ # multi_email_association — the concern skips adding the uniqueness
52
+ # validator (the emails table enforces uniqueness instead).
53
+ refute MultiEmailUser.validators_on(:email).any? { |v|
54
+ v.is_a?(ActiveRecord::Validations::UniquenessValidator)
55
+ }, 'Expected NO UniquenessValidator on MultiEmailUser#email'
56
+ end
57
+ end
58
+
59
+ # -----------------------------------------------------------------------
60
+ # Successful registration
61
+ # -----------------------------------------------------------------------
62
+ describe 'successful registration' do
63
+ before do
64
+ post '/auth', params: registration_params
65
+ @resource = assigns(:resource)
66
+ @data = JSON.parse(response.body)
67
+ end
68
+
69
+ test 'request is successful' do
70
+ assert_equal 200, response.status
71
+ end
72
+
73
+ test 'user is persisted' do
74
+ assert @resource.id
75
+ end
76
+
77
+ test 'resource is a standard User' do
78
+ assert_equal User, @resource.class
79
+ end
80
+
81
+ test 'response includes email' do
82
+ assert @data['data']['email']
83
+ end
84
+ end
85
+
86
+ # -----------------------------------------------------------------------
87
+ # Duplicate email registration is rejected at the model level
88
+ # -----------------------------------------------------------------------
89
+ describe 'duplicate email registration' do
90
+ before do
91
+ @email = Faker::Internet.unique.email
92
+ create(:user, email: @email, provider: 'email').tap(&:confirm)
93
+
94
+ post '/auth', params: registration_params(email: @email)
95
+ @data = JSON.parse(response.body)
96
+ end
97
+
98
+ test 'request is rejected' do
99
+ assert_equal 422, response.status
100
+ end
101
+
102
+ test 'errors mention email taken' do
103
+ assert_not_empty @data['errors']
104
+ end
105
+ end
106
+
107
+ # -----------------------------------------------------------------------
108
+ # OAuth user + same email re-used for email-provider registration
109
+ # (uniqueness is scoped to provider)
110
+ # -----------------------------------------------------------------------
111
+ describe 'email re-use across providers' do
112
+ before do
113
+ @oauth_user = create(:user, :facebook, :confirmed)
114
+
115
+ post '/auth',
116
+ params: registration_params(email: @oauth_user.email)
117
+
118
+ @resource = assigns(:resource)
119
+ @data = JSON.parse(response.body)
120
+ end
121
+
122
+ test 'registration is successful' do
123
+ assert_equal 200, response.status
124
+ end
125
+
126
+ test 'a new email-provider user is created' do
127
+ assert @resource.id
128
+ assert_equal 'email', @resource.provider
129
+ end
130
+ end
131
+
132
+ # -----------------------------------------------------------------------
133
+ # Mang (another standard model, at /mangs)
134
+ # -----------------------------------------------------------------------
135
+ describe 'alternate standard model (Mang)' do
136
+ before do
137
+ post '/mangs', params: registration_params
138
+ @resource = assigns(:resource)
139
+ @data = JSON.parse(response.body)
140
+ end
141
+
142
+ test 'registration is successful' do
143
+ assert_equal 200, response.status
144
+ end
145
+
146
+ test 'resource is a Mang' do
147
+ assert_equal Mang, @resource.class
148
+ end
149
+ end
150
+
151
+ describe 'Mang duplicate email is rejected at model level' do
152
+ before do
153
+ @email = Faker::Internet.unique.email
154
+ create(:mang_user, email: @email, provider: 'email').tap(&:confirm)
155
+
156
+ post '/mangs', params: registration_params(email: @email)
157
+ @data = JSON.parse(response.body)
158
+ end
159
+
160
+ test 'request is rejected' do
161
+ assert_equal 422, response.status
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # MultiEmailUser demonstrates a model that uses devise-multi_email alongside
4
+ # DeviseTokenAuth. Email uniqueness is enforced via the emails association/table
5
+ # (managed by Devise::MultiEmail::ParentModelExtensions) rather than by the
6
+ # column-level uniqueness validation that standard models carry.
7
+ #
8
+ # IMPORTANT ordering rules:
9
+ # 1. has_many :emails must be declared BEFORE the multi_email devise modules so
10
+ # Devise::MultiEmail::ParentModelExtensions can reflect on the association.
11
+ # 2. The multi_email devise modules must be called BEFORE including
12
+ # DeviseTokenAuth::Concerns::User so the concern skips its own devise call
13
+ # (it checks method_defined?(:devise_modules) to decide whether to call devise).
14
+ class MultiEmailUser < ActiveRecord::Base
15
+ # 1. Association first — ParentModelExtensions reflects on it at include time.
16
+ # Rails infers the FK as `multi_email_user_id` from the parent class name.
17
+ has_many :emails,
18
+ class_name: 'MultiEmailUserEmail',
19
+ dependent: :destroy
20
+
21
+ # 2–3 only apply when running with ActiveRecord. In Mongoid runs the
22
+ # activerecord gem is still bundled (so ActiveRecord::Base exists) but
23
+ # active_record/railtie is not required, which means devise-multi_email's
24
+ # AR-specific modules are not registered and calling `devise` would raise
25
+ # NoMethodError. We guard here so the file can be safely autoloaded in
26
+ # Mongoid mode (Zeitwerk still gets a valid MultiEmailUser constant).
27
+ if DEVISE_TOKEN_AUTH_ORM == :active_record
28
+ # 2. multi_email devise modules — include Devise::MultiEmail::ParentModelExtensions
29
+ # which adds multi_email_association, find_by_email, and related helpers.
30
+ devise :multi_email_authenticatable, :registerable,
31
+ :recoverable, :multi_email_validatable, :multi_email_confirmable
32
+
33
+ # 3. DeviseTokenAuth concern — sees devise_modules already defined, skips
34
+ # its own devise call, and adds token management, OmniAuth callbacks, etc.
35
+ include DeviseTokenAuth::Concerns::User
36
+
37
+ # 4. Include the delegated `email` method in JSON serialization so that API
38
+ # responses include the primary email even though there is no email column
39
+ # on this table (email lives in the multi_email_user_emails table).
40
+ def as_json(options = {})
41
+ options[:methods] = Array(options[:methods]) | [:email]
42
+ super(options)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Email record for MultiEmailUser. Each row represents one email address
4
+ # that belongs to a MultiEmailUser.
5
+ #
6
+ # The `primary` boolean column is required by the devise-multi_email gem:
7
+ # it generates `primary?` and `primary=` via ActiveRecord from that column name,
8
+ # which ParentModelManager calls to find/set the primary email address.
9
+ #
10
+ # Devise::MultiEmail::EmailModelExtensions (and EmailValidatable) are included
11
+ # automatically into this class by MultiEmailUser's ParentModelExtensions when
12
+ # that model is first loaded — no explicit include is needed here.
13
+ #
14
+ # The association name (:user) must match Devise::MultiEmail.parent_association_name
15
+ # (which defaults to :user). We specify the class and FK explicitly since our
16
+ # parent model is MultiEmailUser (not User) and the FK column is multi_email_user_id.
17
+ class MultiEmailUserEmail < ActiveRecord::Base
18
+ belongs_to :user,
19
+ class_name: 'MultiEmailUser',
20
+ foreign_key: :multi_email_user_id
21
+ end
@@ -39,7 +39,12 @@ module Dummy
39
39
  config.autoload_paths << Rails.root.join('lib')
40
40
  config.autoload_paths += ["#{config.root}/app/#{DEVISE_TOKEN_AUTH_ORM}"]
41
41
 
42
- config.active_record.legacy_connection_handling = false if Rails::VERSION::MAJOR == 6
42
+ # legacy_connection_handling was deprecated in Rails 7.0/7.1 and removed in Rails 7.2+
43
+ # Only applies when using ActiveRecord (not when using Mongoid).
44
+ if DEVISE_TOKEN_AUTH_ORM == :active_record &&
45
+ !(Rails::VERSION::MAJOR > 7 || (Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR >= 2))
46
+ config.active_record.legacy_connection_handling = false
47
+ end
43
48
 
44
49
  if DEVISE_TOKEN_AUTH_ORM == :mongoid
45
50
  Mongoid.configure do |config|
@@ -2,7 +2,21 @@
2
2
 
3
3
  Rails.application.config.middleware.use OmniAuth::Builder do |b|
4
4
  provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], scope: 'email,profile'
5
- provider :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET']
5
+ provider :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'],
6
+ setup: lambda { |env|
7
+ req = Rack::Request.new(env)
8
+
9
+ # Persist request params so callback can read them later
10
+ env['rack.session']['dta.omniauth.params'] = req.params.slice(
11
+ 'auth_origin_url',
12
+ 'omniauth_window_type',
13
+ # include any additional permitted params you support:
14
+ 'favorite_color'
15
+ )
16
+
17
+ # If your code expects auth data too:
18
+ # env['rack.session']['dta.omniauth.auth'] = ...
19
+ }
6
20
  provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET']
7
21
  provider :apple, ENV['APPLE_CLIENT_ID'], '', { scope: 'email name', team_id: ENV['APPLE_TEAM_ID'], key_id: ENV['APPLE_KEY'], pem: ENV['APPLE_PEM'] }
8
22
  provider :developer,
@@ -22,6 +22,14 @@ Rails.application.routes.draw do
22
22
 
23
23
  mount_devise_token_auth_for 'ConfirmableUser', at: 'confirmable_user_auth'
24
24
 
25
+ # MultiEmailUser uses devise-multi_email to manage email addresses via a
26
+ # separate emails association rather than a single email column.
27
+ # Only mount in ActiveRecord mode — the model's devise setup is AR-only.
28
+ if DEVISE_TOKEN_AUTH_ORM == :active_record
29
+ mount_devise_token_auth_for 'MultiEmailUser', at: 'multi_email_auth',
30
+ skip: [:omniauth_callbacks]
31
+ end
32
+
25
33
  # test namespacing
26
34
  namespace :api do
27
35
  scope :v1 do
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ include MigrationDatabaseHelper
4
+
5
+ class DeviseTokenAuthCreateMultiEmailUsers < ActiveRecord::Migration[7.0]
6
+ def change
7
+ create_table(:multi_email_users) do |t|
8
+ ## Database authenticatable
9
+ t.string :encrypted_password, null: false, default: ''
10
+
11
+ ## Recoverable
12
+ t.string :reset_password_token
13
+ t.datetime :reset_password_sent_at
14
+ t.string :reset_password_redirect_url
15
+ t.boolean :allow_password_change, default: false
16
+
17
+ ## Rememberable
18
+ t.datetime :remember_created_at
19
+
20
+ ## Confirmable
21
+ t.string :confirmation_token
22
+ t.datetime :confirmed_at
23
+ t.datetime :confirmation_sent_at
24
+ t.string :unconfirmed_email
25
+
26
+ ## User Info
27
+ t.string :name
28
+ t.string :nickname
29
+ t.string :image
30
+
31
+ ## Unique oauth id
32
+ t.string :provider
33
+ t.string :uid, null: false, default: ''
34
+
35
+ ## Tokens
36
+ if json_supported_database?
37
+ t.json :tokens
38
+ else
39
+ t.text :tokens
40
+ end
41
+
42
+ t.timestamps
43
+ end
44
+
45
+ add_index :multi_email_users, [:uid, :provider], unique: true
46
+ add_index :multi_email_users, :reset_password_token, unique: true
47
+ add_index :multi_email_users, :confirmation_token, unique: true
48
+ end
49
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DeviseTokenAuthCreateMultiEmailUserEmails < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table(:multi_email_user_emails) do |t|
6
+ t.references :multi_email_user, null: false, foreign_key: true, type: :bigint
7
+ t.string :email, null: false
8
+ # devise-multi_email gem requires a column named `primary` (boolean).
9
+ # The gem calls `email_record.primary?` and `email_record.primary = value`
10
+ # which are auto-generated by ActiveRecord from this column name.
11
+ t.boolean :primary, null: false, default: false
12
+
13
+ # Confirmable columns — required by :multi_email_confirmable which
14
+ # includes devise :confirmable into each email record.
15
+ t.string :confirmation_token
16
+ t.datetime :confirmed_at
17
+ t.datetime :confirmation_sent_at
18
+ t.string :unconfirmed_email
19
+
20
+ t.timestamps
21
+ end
22
+
23
+ add_index :multi_email_user_emails, :email, unique: true
24
+ add_index :multi_email_user_emails, :confirmation_token,
25
+ name: 'index_multi_email_user_emails_on_confirmation_token', unique: true
26
+ add_index :multi_email_user_emails, [:multi_email_user_id, :primary],
27
+ name: 'index_multi_email_user_emails_on_user_and_primary'
28
+ end
29
+ end