Overriding Devise to Skip Setting of Password (Rails4, Devise3)
I’ve been thinking about a light-touch workflow which allows a prospective user to register without setting a password. My first idea was to override the confirmable module (i.e. to invite the user to set a password as part of the confirmation) but I dropped it as it introduces an interim state (an unconfirmed account) and for me this is an unnecessary complication. My idea was to make the registration simpler and requiring a confirmation would be the exact opposite.
Thus I decided to override the default registration workflow so that Devise sets a password which is sent to the user. The user may decide than to change it or not - it would be the usual case of password management. There is a slight imperfection - the password is communicated in plain text. I know that this is not kosher but I decided I’d take this imperfection as I really want to achieve a light-touch registration interaction.
This lengthy blog post is, essentially, a tutorial how to do it - I’ve written it up as I couldn’t find an exact recipe so had to go through an try-and-error process till I got it working.
Starting with a default Devise example
So let’s start first with a relatively simple yet self-contained code example - Daniel Kehoe’s excellent Devise tutorial. I forked it here and simplified it a bit. The example uses Mandrill to handle the emails so you would need to register - a free account gives you a generous quota of 12K emails per month.
Let’s clone that commit. This is done by
git clone https://github.com/nikolay12/my_devise
followed by
git reset --soft 74a92e4ce4934f3e06a753c2d93e2127930192d0
You need than to run
bundle install
The example uses the econfig gem to load the environment variables and database credentials. I’m storing these in secrets.yml (you just need to enter your values)
development:
MANDRILL_USERNAME: ""
MANDRILL_API_KEY: ""
MANDRILL_DOMAIN: ""
RETURN_EMAIL: ""
DEVISE_SECRET: ""
secret_key_base: ""
and database.yml:
development:
adapter: postgresql
encoding: utf8
database: my_devise
pool: 5
username:
password:
host: 127.0.0.1
port: 5432
The example uses postgres but you could use any other DB (in fact, the mysql gem is included in the Gemfile, too). Once the DB credentials are set you need to run
rake db:setup
If you’ve done everything correctly running
rails server
should give you
Adding a customized RegistrationsController
As next we need to override the default RegistrationsController:
class RegistrationsController < Devise::RegistrationsController
def new
super
end
def edit
super
end
def destroy
super
end
def cancel
super
end
# POST /resource
def create
build_resource(sign_up_params)
generated_password = Devise.friendly_token.first(8)
resource.password = generated_password
resource.save
yield resource if block_given?
if resource.persisted?
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_flashing_format?
sign_up(resource_name, resource)
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format?
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
set_minimum_password_length
respond_with resource
end
MyMailer.welcome(resource, {password: generated_password}).deliver if resource.persisted?
end
def update
super
end
end
Instead of typing/pasting the above code you can copy
/app/controllers/registrations_controller.rb
from the github repository but you’ll need to rebase it to include the latest commit (if you reset to the older commit as suggested above):
git reset HEAD@{1}
If you haven’t reset your local git repo than you wouldn’t need to do anything - a simple git clone has the code you need.
Adding customized views for the RegistrationsController
These are, basically, the default views which can be generated by
rails generate devise:views
However, they need to be placed under
app/views/registrations
A simple copy is what you need:
cp app/views/devise/registrations/* app/views/registrations/
Than you can remove the password/password confirmation fields.
Changing the routes to account for the customized RegistrationsController
The other controller-related change that is necessary is to set the route to:
devise_for :users, :controllers => { :registrations => "registrations" }
Set a custom Devise mailer
The other remaining task is to set a cusomized mailer that sends the password upon registration. It is called by the RegistrationsController abobe. The mailer is simple:
#/app/mailers/my_mailer.rb
class MyMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
def welcome(record, opts={})
devise_mail(record, :welcome, opts)
end
end
You also need to add the views and place them under the corresponding views sub-directory. You can copy the default views:
cp app/views/devise/mailer/* app/views/mymailer
and add the view that contains the password:
#app/views/my_mailer/welcome.html.erb
<p>Welcome!</p>
<p>We've generated a password for you: <%= @resource.password %></p>
<p>If you prefer, please, feel free to change it (under "Account/Settings").</p>
That’s it!