allenwq / devise-multi_email

✉️ Let devise support multiple emails (authenticatable, confirmable and validatable).
MIT License
77 stars 29 forks source link
devise rails ruby

Devise::MultiEmail Build Status Coverage Status

Letting Devise support multiple emails, allows you to:

:multi_email_authenticatable, :multi_email_confirmable and :multi_email_validatable are provided by _devise-multiemail.

Getting Started

Add this line to your application's Gemfile:

gem 'devise-multi_email'

Suppose you have already setup Devise, your User model might look like this:

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable
end

In order to let your User support multiple emails, with _devise-multiemail what you need to do is just:

class User < ActiveRecord::Base
  has_many :emails

  # Replace :database_authenticatable, with :multi_email_authenticatable
  devise :multi_email_authenticatable, :registerable
end

class Email < ActiveRecord::Base
  belongs_to :user
end

Note that the :email column should be moved from users table to the emails table, and a new primary boolean column should be added to the emails table (so that all the emails will be sent to the primary email, and user.email will give you the primary email address). Your emails table's migration should look like:

create_table :emails do |t|
  t.integer :user_id
  t.string :email
  t.boolean :primary
 end

You can choose whether or not users can login with an email address that is not the primary email address.

Devise::MultiEmail.configure do |config|
  # Default is `false`
  config.only_login_with_primary_email = true
end

The autosave is automatically enabled on the emails association by default. This is to ensure the primary flag is persisted for all emails when the primary email is changed. When autosave is not enabled on the association, only new emails are saved when the parent (e.g. User) record is saved. (Updates to already-persisted email records are not saved.)

If you don't want autosave to be enabled automatically, you can disable this feature. What this will do is enable alternative behavior, which adds an after_save callback to the parent record and calls email.save on each email record where the primary value has changed.

Devise::MultiEmail.configure do |config|
  # Default is `true`
  config.autosave_emails = false
end

Configure custom association names

You may not want to use the association user.emails or email.users. You can customize the name of the associations used. Add your custom configurations to an initializer file such as config/initializers/devise-multi_email.rb.

Note: model classes are inferred from the associations.

Devise::MultiEmail.configure do |config|
  # Default is :user for Email model
  config.parent_association_name = :team
  # Default is :emails for parent (e.g. User) model
  config.emails_association_name = :email_addresses
  # Default is :primary_email_record
  config.primary_email_method_name = :primary_email
end

# Example use of custom association names
team = Team.first
emails = team.email_addresses

email = EmailAddress.first
team = email.team

Confirmable with multiple emails

Sending separate confirmations to each email is supported. What you need to do is:

Declare devise :multi_email_confirmable in your User model:

class User < ActiveRecord::Base
  has_many :emails

  # You should not declare :confirmable and :multi_email_confirmable at the same time.
  devise :multi_email_authenticatable, :registerable, :multi_email_confirmable
end

Add :confirmation_token, :confirmed_at and :confirmation_sent_at to your emails table:

create_table :emails do |t|
  t.integer :user_id
  t.string :email
  t.boolean :primary, default: false

  ## Confirmable
  t.string :unconfirmed_email
  t.string :confirmation_token
  t.datetime :confirmed_at
  t.datetime :confirmation_sent_at
end

Then all the methods in Devise confirmable are available in your Email model. You can do email#send_confirmation_instructions for each of your email. And user#send_confirmation_instructions will be delegated to the primary email.

Validatable with multiple emails

Declare devise :multi_email_validatable in the User model, then all the user emails will be validated:

class User < ActiveRecord::Base
  has_many :emails

  # You should not declare :validatable and :multi_email_validatable at the same time.
  devise :multi_email_authenticatable, :registerable, :multi_email_validatable
end

You can find the detailed configurations in the rails 5 example app.

ActiveJob Integration

The Devise README describes how to use ActiveJob to deliver emails in the background. Normally you would place the following code in your User model, however when using _devise-multiemail you should place this in the Email model.

# models/email.rb
def send_devise_notification(notification, *args)
  devise_mailer.send(notification, self, *args).deliver_later
end

What's more

The gem works with all other Devise modules as expected -- you don't need to add the "multi_email" prefix.

class User < ActiveRecord::Base
  devise :multi_email_authenticatable, :multi_email_confirmable, :multi_email_validatable, :lockable,
         :recoverable, :registerable, :rememberable, :timeoutable, :trackable
end

Issues

You need to implement add/delete emails for a user as well as set/unset "primary" for each email.

You can do email.send_confirmation_instructions for each email individually, but you need to handle that logic in some place (except for the primary email, which is handled by Devise by default). e.g. After a new email was added by a user, you might want to provide some buttons in the view to allow users to resend confirmation instructions for that email.

Wiki

Migrating existing user records

Development

After checking out the repo, run bundle install to install dependencies.

Then, run bundle exec rake to run the RSpec test suite.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/allenwq/devise-multi_email. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.