James Kiefer Home Categories Posts Projects
Email Me Github

Gradual engagement with Rails 5 & Devise 4

This is going to be a quick overview on how to alter devise to allow for email only registration flow. You should be able to adapt this process to accomodate most gradual engagement setups.

Here are the basic steps:

Overload password verification methods in the user model

Add these methods to your user model.

def password_required?
  super if confirmed?

def password_match?
  self.errors[:password] << "can't be blank" if password.blank?
  self.errors[:password_confirmation] << "can't be blank" if password_confirmation.blank?
  self.errors[:password_confirmation] << "does not match password" if password != password_confirmation
  password == password_confirmation && !password.blank?

Remove password fields from registration template

Remove the password fields from app/views/devise/registrations/new.html.erb, here is what mine looked like:

<div class="field">
  <%= f.label :password %>
  <% if @minimum_password_length %>
  <em>(<%= @minimum_password_length %> characters minimum)</em>
  <% end %><br />
  <%= f.password_field :password, autocomplete: "new-password" %>
 <div class="field">
  <%= f.label :password_confirmation %><br />
  <%= f.password_field :password_confirmation, autocomplete: "new-password" %>

Extend confirmations controller to allow a two step confirmation

Create dsr/app/controllers/confirmations_controller.rb:

class ConfirmationsController < Devise::ConfirmationsController
  def show
    if params[:confirmation_token].present?
      @original_token = params[:confirmation_token]
    elsif params[resource_name].try(:[], :confirmation_token).present?
      @original_token = params[resource_name][:confirmation_token]

    self.resource = resource_class.find_by_confirmation_token @original_token

  def confirm
    @original_token = params[resource_name].try(:[], :confirmation_token)

    self.resource = resource_class.find_by_confirmation_token! @original_token
    resource.assign_attributes(permitted_params) unless params[resource_name].nil?

    if resource.valid? && resource.password_match?
      set_flash_message :notice, :confirmed
      sign_in_and_redirect resource_name, resource
      render :action => 'show'

   def permitted_params
     params.require(resource_name).permit(:confirmation_token, :password, :password_confirmation)

Create dsr/app/views/devise/confirmations/show.html.erb:

<h2>You're almost done! Now create a password to securely access your account.</h2>
<%= form_for(resource, as: resource_name, url: confirm_path, html: { method: :patch }) do |f| %>
  <%= devise_error_messages! %>

  <div class="field">
    <%= f.label :password %><br />
    <%= f.password_field :password, autofocus: true %>

  <div class="field">
    <%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation, autofocus: true %>

  <%= f.hidden_field :confirmation_token, :value => @original_token %>

  <div class="actions">
    <%= f.submit "Sign up" %>
<% end %>

<%= render "devise/shared/links" %>

Alter the devise routes to use our new extended controller:

devise_for :users, :controllers => {:confirmations => 'confirmations'}
devise_scope :user do
  patch "/confirm" => "confirmations#confirm"

Thank you

Your comment has been submitted and will be published once it has been approved.


Your comment has not been submitted. Please go back and try again. Thank You!

If this error persists, please open an issue by clicking here.

Say something