Mappa Via Marconi 20, Bussolengo (VR)
Email info@devinterface.com

Two step signup with Devise

[** UPDATE: 21/01/2013 **]: Thanks to Kevin Triplett now there's a Devise wiki page referencing this post and combining the comments. Please check this wiki page, especially if you’re using Rails 3 and Devise 2:
https://github.com/plataformatec/devise/wiki/How-To:-Email-only-sign-up

On my Ruby on Rails projects I usually use the Devise gem for user authentication.

In the last application I need to customize Devise so that users can register providing email address only. I will ask for a password only at the confirmation step.
After some tests, I come with this solutions.

1. First I need to overwrite the ConfirmationsController, so in the routes.rb I set devise to use my custom controller (my model is called Account).
I also need to define a custom controller for registrations so I can customize the registration view:


devise_for :accounts, :controllers => {:confirmations => "confirmations", :registrations => "registrations"} do
put "confirm_account", :to => "confirmations#confirm_account"
end

As you can see I've also added a custom method, confirm_account that I will use in the fourth step, as you will see soon.

2. Now I need to skip the devise built in password validation.
To achieve this I wrote a custom initializer called devise_customization.rb in /config/initalizers/ like this:


module Devise
module Models
module Validatable

def password_required?
false
end

 

 end
end
end

So I have overwritten the default devise validatable module skipping the password validation.

3.The next step is to customize views.
Given I'm using a custom ConfirmationsController and RegistrationsController I have copied the default confirmations views under /views/confirmations/ and the default registrations views under /views/registrations/ and changed them according to my two step registration.

The /registrations/new view is something like this (I'm using haml)


= form_for(resource, :as => resource_name, :url => account_registration_path(@account)) do |f|
= devise_error_messages!
%p
= f.label :email
= f.email_field :email
%p.clearfix
= f.submit 'Signup'
= link_to 'Home', root_url
%br/
= render :partial => 'devise/shared/links'

Here I've just remove the password and password_confirmation fields.

The /confirmations/show view is something like this


= form_for(resource, :url => confirm_account_path) do |f|
= devise_error_messages!
%p
= f.label :email
= @account.email
= f.hidden_field :confirmation_token
%p
= f.label :password
%br/
= f.password_field :password
%p
= f.label :password_confirmation
%br/
= f.password_field :password_confirmation
%p.clearfix
= f.submit 'Confirm Account'
= link_to 'Home', root_url
%br/
= render :partial => 'devise/shared/links'

4. Ok, once I've setup the views I have to write the new confirmation method in my ConfirmationsController. I can leave unchanged the RegistrationsController.


class ConfirmationsController < Devise::ConfirmationsController
def show
@account = Account.find_by_confirmation_token(params[:confirmation_token])
if !@account.present?
render_with_scope :new
end
end

def confirm_account
@account = Account.find(params[:account][:confirmation_token])
if @account.update_attributes(params[:account]) and @account.password_match?
@account = Account.confirm_by_token(@account.confirmation_token)
set_flash_message :notice, :confirmed
sign_in_and_redirect("account", @account)
else
render :action => "show"
end
end

 

end

The show method simply find the account to confirm by the devise token and render the show view.
The key point is the confirm_account method where I find the account, update attributes and if password_match? return true I confirm the account calling the standard devise method confirm_by_token

5. The last thing to do is to define the password_match? method inside the Account model.


class Account < ActiveRecord::Base
...

def password_match?
self.errors[:password] << 'password not match' if password != password_confirmation
self.errors[:password] << 'you must provide a password' if password.blank?
password == password_confirmation and !password.blank?
end

 

end

That's all!
Now you can star your rails server and signup in your application providing a password only at the confirmation step.