I don’t believe email confirmation or password confirmation are desirable in web apps.
We wrote and maintain a user authentication and authorization Rails engine called Clearance. It serves a narrow focus for users to sign up, sign in, sign out, and reset their password, all using their email address and a password of their choice.
Clearance is almost 3 years old but we still use it on almost all of our projects. Each new project is an opportunity to re-evaluate what’s good and what sucks about Clearance.
Early on, our goal was mostly a great experience for developers, seen in things like:
Lately, our goal is a great experience for users, seen in things like:
See the CHANGELOG for more. The most unusual decisions were the confirmation decisions.
When you sign up, you’re interacting with the product for the first time. Maybe you’ve heard good things from friends and have skimmed the landing page. Now, you want to try it. Your purpose is to get your hands on it.
So, you enter your email and password, then press “sign up”. If you’re presented with a screen like “please check your email to confirm your account”, you’re kind of bummed.
Maybe you have GMail open in another tab or maybe you’re an ⌘+Tab master and you flick over to Mail.app real quick, see the confirmation email, open it, click the confirmation link, which opens a new tab in your browser, signing you in and presenting you with a screen like “thanks for confirming your account”.
You just took about five actions, made about three decisions, and this is if you’re very savvy and already have your email client open. That, to me, is a burden. Why on earth would you want to get your relationship started with a potential customer by burdening them?
We have numbers that show you lose customers this way. In one example, we have a consumer app with 9,204 email sign ups, but only 4,607 confirmations.
That’s an atrocious 50% conversion rate between a user declaring their intent to try the product and actually trying it.
By removing the email confirmation step, our conversion rate goes to 100% now and forever. Meanwhile, the user has only had to enter two fields, press “enter” or a “sign up” button, and they’re immediately signed in and using the product.
… someone mistypes their email address?
We think what we’ve provided now is a good user experience but you could argue that making sure the app can easily recover when someone mistypes their email address is also providing a good user experience.
For the moment, I’m trying to not let edge cases dictate how the majority of users interact with the product.
Have you ever enjoyed typing your password twice when you signed up for a product? The answer for most people is “no”, and that should drive this decision.
… someone mistypes their password?
By default, Clearance signs users in for a year. How often will people need to use their password? It depends on your app, but likely very rarely.
There’s also a password reset, which incidentally does rely on email confirmation. In this case, I hope the extra steps evoke more of a feeling of “that makes sense, they’re securing my account for me, someone who’s invested time into their product”.
We’ll continue to test how people feel about the experience in each real product where we use Clearance. If my hypothesis that people feel good about the email confirmation on password reset turns out to be incorrect, I’ll experiment with the default copy in the flash messages and email body to see if we can move the needle on user’s feelings that way before changing the feature.
I’m excited about these user experience-driven changes to our open source library and I hope you’ll put them to the test.
If you’re already using an old version of Clearance, please read the upgrade instructions. Please also follow Clearance on Github.
Happy coding.
Have you ever analyzed the “access control” (also called “authorization”) options in Rails and thought, “something better must be out there”?
I’ve analyzed libraries and concluded “authorization needs to be custom in each app.” Many will disagree.
If you’ve reached the same conclusion, however, your decisions are not over:
I’m happily considering the following approach as my new standard. What do you think?
map.resources :accounts, :only => [:new, :create, :show]
map.resources :brands, :only => [:new, :create, :show] do |brands|
brands.resources :offers, :only => [:new]
end
Brands belong to accounts. Offers belong to brands. Users belong to accounts.
Prefer flat routes (and no subdomains) when at all possible. It keeps the mental overhead low everywhere in your app. Read: new_brand_path versus new_account_brand_path(@account). Stop forgetting to include :account_id as an a parameter in your controller specs / functional tests.
We’re using the domain model to simplify the app’s implementation. Users are authenticated using Clearance. They’ve got a simple account_id foreign key.
This means that with an authenticated user in a typical “account” application, we can lean on Clearance’s :authenticate before filter and regular ActiveRecord finders.
class BrandsController < ActionController
before_filter :authenticate
def new
@brand = current_user.account.brands.build
end
def create
@brand = current_user.account.brands.build(params[:brand])
# ...
end
def show
@brand = current_user.account.brands.find(params[:id])
end
end
This is repetitive, however… and who writes RESTful controllers by hand these days? Check out how nice the same thing looks with Inherited Resources:
class BrandsController < InheritedResources::Base
before_filter :authenticate
actions :new, :create, :show
protected
def begin_of_association_chain
current_user.account
end
end
With this pattern, we do not have to test access control on the new and create actions because the user will only be able to create brands for their account.
Looking back at the routes and our domain model, however, we will still need to test access control for creating offers that belong to brands.
should "not find brands not associated with signed in user" do
brand = Factory(:brand)
sign_in_as Factory(:user)
assert_raises(ActiveRecord::RecordNotFound) do
get :new, :brand_id => brand.to_param
end
end
Done for the new action.
Rails returns a 404 when ActiveRecord::RecordNotFound is raised. This error will be raised in our access control scheme because there is no record of the current_user having a relationship to this brand. Pretty reasonable, right?
Let’s get to green:
class OffersController < InheritedResources::Base
before_filter :authenticate
belongs_to :brand
def begin_of_association_chain
current_user
end
end
How did this last association chain work? Inherited Resources knows Offers belong to Brands and to start the chain with current_user. This is the equivalent of:
@brand = current_user.brands.find(params[:brand_id])
@offer = @brand.offers.build
If you were really paying attention, you noticed that User belongs to accounts and Account has many brands. I could have said current_user.account but I kept the chain from the perspective of the controller shorter using delegation:
class User < ActiveRecord::Base
include Clearance::User
belongs_to :account
delegate :brands, :to => :account
end
This may seem pedantic but it will make my life easier when the rules around users’ relationship to brands get more complex, which is in the plans based on stories in Pivotal Tracker…
I’m digging this approach from a few perspectives. It is not a lot of lines of code. It leans heavily on the framework, stays DRY, and uses normal authentication and RESTful conventions. It’s easy to test and I know where those tests should go.
What do you think?
On Fridays, we enjoy getting together for lunch and reviewing code which needs tender loving care. This week, our liveliest discussion surrounded responsibility for authorization logic.
You’ve come across it before ~ you have a User model and a second model for which Users must be authorized to perform certain tasks:
def show
@event = Event.find params[:id]
unless (logged_in? &&
(@event.members.include?(current_user) ||
@event.organizer.include?(current_user)) ||
current_user.admin?)
flash[:failure] = "You are not authorized to view this event."
redirect_to home_url and return
end
end
In discussion, we decided there is too much logic in the controller. We want to move the authorization into the correct place in our domain model. But where?
unless current_user.authorized_for_event?(@event)
That reads nicely in English as “Unless the current user is authorized for the event” but logically, a User should not be responsible for its own authorization. If that were the case in the real world, we’d have societal chaos:
Kaffee: Did you order the code red?!
Col. Jessep: You’re goddamn right I did!

Whether your domain model has an Event or a CodeRed, the authorization logic should be on those models, not on User. In just about every case I can think of, that will remain true.

For example, I have a ticket to a Red Sox playoff game. When I arrive on Yawkey Way, the ticket-taker will authorize me to enter the event. He or she is the Controller in our MVC pattern (and I suppose the View is the world of sight, touch, taste, sound, and smell…). The ticket-taker doesn’t need to know that I paid with a MasterCard on September 20th or that my seat is in right field or any other details. All that matters is: “is this guy authorized?”
def attend
@game = RedSoxGame.find_by_id params[:id]
unless @game.authorized?(current_user)
flash[:failure] = "You are not authorized for this game.
Watch it over a pint of Sam Adams at the Cask n' Flagon."
redirect_to home_url and return
end
end
class RedSoxGame < ActiveRecord::Base
def authorized?(user)
return false if user.nil?
self.doors_open? && self.not_rained_out?
&& user.ticket?(self) && user.not_drunk?
end
end
The authorized method is noteworthy for two reasons. First, it uses the Composed Method:
Divide your program into methods that perform one identifiable task. Keep all of the operations in a method at the same level of abstraction. This will naturally result in programs with many small methods, each a few lines long.
The authorized? method is very easy to read because it focuses on simply authorizing the user for the game. It relies on a number of other methods whose purpose is clear and are similarly of narrow focus.
The second thing to note is that authorized? actually calls methods on the User model. It may seem contradictory to call User after saying that authorization logic does not belongs there. However, it isn’t authorization logic, it’s another level of abstraction.
Most importantly, the ticket-taker will let me into the Sox game and no one gets a code red.