Jack Moore

Email: jack(at)jmoore53.com
Project Updates

Custom Devise Mailer

16 Nov 2019 » rails, mailer, custom, backend

An Admin Dashboard should be able to create users and other admins, and the application should send the new user a one-time confirmation link as well as initial password for the new account. This post covers disabling admin and user sign up, but allowing currently signed in admins to create these new users. The Devise gem offers customizations to use a custom mailer and an ability to pull this off with some quick hacking.

Please note, this will cover only adding admin, however the same process can be followed for user to achieve similiar results.

Routes

On installation, Devise by default offers routing with a devise_for block and this takes care of most of what we need, however there is some customization needed to both block routes that should not be public and ensuring our routing goes to the custom controller.

Rails.application.routes.draw do
  devise_for :admins, skip: [:registrations]

  authenticated :admin do
    devise_for :admins, only: [:registrations], controllers: { registrations: 'admins/registrations' }
    root to: "staticpages#index", as: :authenticated_user
    resources :admins, only: [:index, :show]
  end
end

Looking at this block, please note that everything except registrations our adminstrative user must be logged in for. This means that the user is able to do everything normally except the registration routes. Almost all the defaults are still in place (sign_in, any kind of change…).

Make sure, make sure, make sure your routes are configured properly or you will run into issues with devise trying map a POST request to the default controller. The route needs to be configured to go to the custom controller!

Custom Devise Controller

Now that we have the devise routes programmed to point to our custom controllers, we have to make edits to our current controllers. The changes look like the following to the app/controllers/admins/registrations_controller.rb

class Admins::RegistrationsController < Devise::RegistrationsController
  before_action :configure_sign_up_params, only: [:create]
  before_action :authenticate_admin!

  # POST /resource
  def create
    generated_password = Devise.friendly_token.first(8)
    @new_admin = Admin.create!(email: sign_up_params[:email], password: generated_password)
    AdminMailer.confirmation_instructions(@new_admin, @new_admin.confirmation_token, {password: generated_password}).deliver_now
    render json: {
      admin:  @new_admin
    }
  end

  # If you have extra params to permit, append them to the sanitizer.
  def configure_sign_up_params
    devise_parameter_sanitizer.permit(:sign_up, keys: [:email])
  end

end

These two methods: create and configure_signup_params are needed to create the new admin by the current admin, and the create method in this class calls the custom mailer to send off the newly generated password, and the confirmation token. This code is not perfect and should really use a job queue, but right now it will do what it is needed, no more and no less.

Overwriting Model

When the Admin.create method is called the default mailer method is then called and the default email is sent out which is wrong! We don’t want this, we want to specify that our controller will be calling the deliver_now method using the custom mailer.

This means we have to overwrite the model’s send_on_create_confirmation_instructions.

class Admin < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :trackable, :confirmable

  def send_on_create_confirmation_instructions
    return nil
  end
end

Right now I have the most basic overwrite on that method to do nothing, but really this is all I need.

Custom Mailer

Ah yes, the custom mailer.

rails g mailer AdminMailer will give us a custom mailer and get us off the ground.

Because this application only emails Admins, in app/config/initializers/devise.rb specifiy that we will be using the admin mailer by changing the config.mailer line to be config.mailer = 'AdminMailer'. This will tell devise to use the AdminMailer class to send emails.

Our AdminMailer Class (app/mailers/admin_mailer.rb) looks like the following:

class AdminMailer < Devise::Mailer
    helper :application
    include Devise::Controllers::UrlHelpers
    default template_path: 'devise/mailer'

    def confirmation_instructions(record, token, opts={})
        @password = opts[:password]
        super
    end
end

Basically all we are doing is customizing the confirmation instructions when a new user is created. The confirmation_instructions method states to set @password to the password we specifiy (this password is passed in the when the controller calls the confirmation_instructions method when the controller’s create method is called).

The view is a very barebones .erb file:

<p>Welcome <%= @email %>!</p>

<p>You can confirm your account email through the link below:</p>

<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>

<p>Your password is <%= @password %>

After setting these we are good to go, and testing it out it appears everything is in working order.

Resources

© Jack Moore