title: Devise Confirmable with Admin Invite
body: <p>Working on updating some legacy devise code (that doesn't use <code>devise_invitable</code>) and suffering the inevitable headache.</p>
  
  <p>The desired functionality is to stave off the delivery of Devise's confirmation email until the admin actually invites the given user, manually.</p>
  
  <p>There's a form on <code>devise/registrations#new</code> where a user inputs their email, and the admin backend, ultimately, <code>users#index</code>, there's an "invite button".</p>
  
  <p>It would seem that things have changed in recent years with devise (app is currently at  <code>~&gt; 4.1.1</code>) since the legacy business logic in this admin dashboard looks for users that do NOT have a <code>confirmation_token</code> present, and asks the admin if they want to send those users an invite.</p>
  
  <pre><code>&lt;%=(user.confirmation_token.nil? ? 
    (link_to "Send Invitation", invite_user_path(user.id), {:class =&gt; 'button radius tiny'}) : 
    (link_to "Resend", invite_user_path(user.id), {:class =&gt; 'button radius tiny'})) unless user.confirmed_at.present?
  %&gt;
  </code></pre>
  
  <p>Here is the <code>User</code> model, chock full of methods meant to override the default logic devise uses to validate and create the user.</p>
  
  <pre><code>class User &lt; ActiveRecord::Base
    ...
    devise :database_authenticatable, :registerable, 
           :confirmable, :recoverable, :rememberable, 
           :trackable, :validatable
  
    has_and_belongs_to_many :roles, :join_table =&gt; :roles_users
  
    after_create :add_role
  
    def password_required?
     if !persisted?
       !(password != "")
     else
       !password.nil? || !password_confirmation.nil?
     end
    end
  
    # override Devise method
    def confirmation_required?
     false
    end
  
    # override Devise method
    def active_for_authentication?
     confirmed? || confirmation_period_valid?
    end
  
    def send_reset_password_instructions
      if self.confirmed?
        super
      else
       errors.add :base, "You must receive an invitation before you set your password."
      end
    end
  
    # new function to set the password
    def attempt_set_password(params)
      p = {}
      p[:password] = params[:password]
      p[:password_confirmation] = params[:password_confirmation]
      update_attributes(p)
    end
  
    # new function to determine whether a password has been set
    def has_no_password?
      self.encrypted_password.blank?
    end
  
    # new function to provide access to protected method pending_any_confirmation
    def only_if_unconfirmed
      pending_any_confirmation {yield}
    end
  
    private
  
    def add_role
      return if roles.pluck(:name).include? "partner"
  
      free = Role.find_by_name "free"
      roles &lt;&lt; free
      save!
    end
  end
  </code></pre>
  
  <p>After tinkering with this on prod, it would seem that the confirmation token is being created immediately, as in when the <code>User</code> record is created.</p>
  
  <pre><code>2016-06-06T21:49:19.283289+00:00 app[web.1]:   
    SQL (27.9ms)  
      UPDATE `users` 
      SET `created_at` = '2016-06-06 21:49:19.172495', 
          `updated_at` = '2016-06-06 21:49:19.172495', 
          `id` = 37, 
          `unconfirmed_email` = 'adminemail@gmail.com', 
          `confirmation_token` = 'bdyrKwZG31mPtfAskEsU', 
          `confirmation_sent_at` = '2016-06-06 21:49:19.252215' 
      WHERE `users`.`id` = 37
  </code></pre>
  
  <p>Interestingly, it's not 100% clear that the email is <em>actually</em> being sent, but I just want to understand why the confirmation token (and <code>confirmation_sent_at</code>) are still being updated immediately. Is that a bug? Is that new? Or am I wrong?</p>
  
  <p>Including the <code>ConfirmationsController</code> spaghetti code too for your viewing pleasure:</p>
  
  <pre><code>class ConfirmationsController &lt; Devise::PasswordsController
  
    # Remove the first skip_before_filter (:require_no_authentication) if you
    # don't want to enable logged users to access the confirmation page.
    # skip_before_filter :require_no_authentication
  
    skip_before_filter :authenticate_user!
  
    # POST /resource/confirmation
    def create
      self.resource = resource_class.send_confirmation_instructions(resource_params)
      if successfully_sent?(resource)
        respond_with({}, :location =&gt; after_resending_confirmation_instructions_path_for(resource_name))
      else
        respond_with(resource)
      end
    end
  
    # PUT /resource/confirmation
    def update
      with_unconfirmed_confirmable do
        if @confirmable.has_no_password?
          @confirmable.attempt_set_password(resource_params)
          if @confirmable.valid?
            @confirmable.update_attributes!(resource_params)
            do_confirm
          else
            do_show
            @confirmable.errors.clear #so that we won't render :new
          end
        else
          self.class.add_error_on(self, :email, :password_allready_set)
        end
      end
  
      if !@confirmable.errors.empty?
        render 'devise/confirmations/new'
      end
    end
  
    # GET /resource/confirmation?confirmation_token=abcdef
    def show
      with_unconfirmed_confirmable do
        if @confirmable.has_no_password?
          do_show
        else
          do_confirm
        end
      end
      if !@confirmable.errors.empty?
        render 'devise/confirmations/new'
      end
    end
  
    protected
  
    def with_unconfirmed_confirmable
      @confirmable = User.find_or_initialize_with_error_by(:confirmation_token, params[:confirmation_token])
      self.resource = @confirmable
      if !@confirmable.new_record?
        @confirmable.only_if_unconfirmed {yield}
      end
    end
  
    def do_show
      @confirmation_token = params[:confirmation_token]
      @requires_password = true
      render 'devise/confirmations/show'
    end
  
    def do_confirm
      @confirmable.confirm!
      set_flash_message :notice, :confirmed
      sign_in_and_redirect(resource_name, @confirmable)
    end
  
    # The path used after resending confirmation instructions.
    def after_resending_confirmation_instructions_path_for(resource_name)
      new_session_path(resource_name)
    end
  
    # The path used after confirmation.
    def after_confirmation_path_for(resource_name, resource)
      after_sign_in_path_for(resource)
    end
  
    private
  
    def resource_params
      params.require(:user).permit(:first_name, :last_name, :email, :encrypted_password, :reset_password_token, :reset_password_sent_at, :remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip, :last_sign_in_ip, :created_at, :updated_at, :confirmation_token, :confirmed_at, :confirmation_sent_at)
    end
  end
  </code></pre>
  
  <p>Adding the <code>RegistrationsController</code> too:</p>
  
  <pre><code>class Users::RegistrationsController &lt; Devise::RegistrationsController
    def new
      super
    end
  
    def create
      session[:email] = params[:user][:email] if params[:user][:email]
  
      if User.exists?(email: params[:user][:email])
        redirect_to :public_feed, flash: { notice: "Welcome back." }
      else
        super
      end
    end
  end
  </code></pre>