giant robots smashing into other giant robots

Written by thoughtbot

dancroak

Email and password confirmation bias

I don’t believe email confirmation or password confirmation are desirable in web apps for early-stage startups.

Clearance

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.

User experience

Early on, our goal was mostly a great experience for developers, seen in things like:

  • Generated Cucumber features
  • Shoulda test helpers
  • Consistent sign_{up,in,out} naming

Lately, our goal is a great experience for users, seen in things like:

  • No email confirmation
  • No password confirmation
  • No 403 Forbidden status code (while technically correct, Chrome and other browsers display a scary screen for 403s)
  • HTML5 email fields (so iPhone users and others get the @ sign on their keyboard)
  • Lowercasing all emails (so iPhone users whose keyboard defaults the first letter to uppercase can still sign in without confusion)

See the CHANGELOG for more. The most unusual decisions were the confirmation decisions.

No email confirmation

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 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 a 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%. 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.

But what if 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 library.

But what if spam becomes a problem?

It’s not a problem yet. As an early-stage startup, your problem is attracting, converting, and retaining users. Focus on that problem first, and deal with spam later.

No password confirmation

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.

But what if 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”.

User experience-driven open source

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.

Written by .

dancroak

Code Cowboy

I’ve written before about lightweight access control using inherited_resource’s begin_of_association_chain and raising a 404.

I still prefer this approach in the vast majority of apps I work on, but it doesn’t work in the case where there is no relationship between a user and an object other than access control.

gunshot

custom

Last week, I had a need to differentiate access control to some controller actions based on whether a signed in user was a teacher or student.

This is a simple need, and I’m loathe to introduce a large gem into the app for what could just be a custom before_filter:

class CoursesController < InheritedResources::Base
  actions :new, :show, :index

  before_filter :authenticate
  before_filter :deny_student, :only => [:new]

  protected

  def deny_student
    deny_access if current_user.student?
  end
end

However, I recently saw acl9 mentioned on a mailing list and really liked the DSL. Couldn’t we have the best of both worlds?

So, Josh Clayton and I wrote up a sub-set of acl9’s DSL that would meet my needs.

module AccessControl
  def self.included(controller)
    controller.extend(ClassMethods)
    controller.send(:include, InstanceMethods)
  end

  module ClassMethods
    def access_control(options = {}, &block)
      before_filter(options) do |controller|
        controller.authenticate
      end

      before_filter(options) do |controller|
        controller.instance_eval(&block)
      end
    end
  end

  module InstanceMethods
    def allow(role, opts = {})
      if opts[:to].nil? || opts[:to].include?(action_name.to_sym)
        unless current_user.send("#{role}?")
          deny_access(opts[:flash])
        end
      end
    end

    def deny(role, opts = {})
      if opts[:from].nil? || opts[:from].include?(action_name.to_sym)
        if current_user.send("#{role}?")
          deny_access(opts[:flash])
        end
      end
    end
  end
end

ActionController::Base.send :include, AccessControl

Gunshot

the DSL

In action:

class CoursesController < InheritedResources::Base
  actions :new, :show, :index

  access_control do
    allow :teacher, :to => [:new]
  end
end

Very expressive. Nice work, Oleg Dashevskii!

strut

features we cared about

  • optional flash mesage
  • natural language (allow/to and deny/from)
  • role is just a boolean on User

opinions we could have

This will usually be included when most actions require a signed in user. Therefore, we call authenticate and allow an ugly override for edge cases:

class CoursesController < InheritedResources::Base
  actions :new, :show, :index

  access_control(:except => :show) do
    allow :teacher, :to => [:new]
  end
end

This is used with Clearance and can therefore rely on authenticate, current_user, and deny_access.

Girl, gun, clock

instance_eval

Writing code with a pretty DSL in mind is exceedingly fun. The one downside seems to be that scope can become confusing.

Yesterday I was messing with Rails app templates and read the source for the 2.3 template runner. It, like this custom access control DSL, also uses instance_eval as the key line to make things work.

In the app template example, it was hard to figure out how to get the file path of the original template.

I simply wanted to require 'helper', a separate file in the app template, but because of the scope within which it was instance_eval‘d, helper could not be found.

in_root { self.instance_eval(code) }

The solution in that case was to take advantage of other public methods and mess with the load path, which would probably an atrocious solution for a vendored gem in a Rails app, but acceptable for a one-off script to generate a Rails app:

here = File.expand_path(File.dirname(template), File.join(root,'..'))
$LOAD_PATH << here
require 'helper'

In the access control example, it was hard to figure out how to have access to flash, redirect_to, etc. from a class-level scope.

Similar to the require 'helper' example, your tests will fail if:

  • deny and access are defined at the class level
  • instance_eval is not used to delay evaluation until runtime
  • you don’t use before_filter’s block

The solution is to use blocks and lazy evaluation to control scope, but it can be confusing to get there:

before_filter(options) do |controller|
  controller.authenticate
end

before_filter(options) do |controller|
  controller.instance_eval(&block)
end

not a code cowboy

Every time you decide to roll your own, remind yourself that you may waste time getting lost in something like a scope problem you haven’t seen before. Then, once you get the rhythm down, be careful of a thought like “it won’t take me that long to write.” Re-inventing the wheel has to be balanced with a specific reason in addition to, “it will be fun for me.”

The third-party ecosystem of gems and plugins is one reason why Rails is awesome. However, writing custom code every now and again is worth it for programmer pleasure, fewer lines of code and dependencies, and staying focused on only what you need.

Ruby makes great DSLs, though, so don’t be afraid to take inspiration from existing code and try your hand at making beautiful code for a specific purpose.

dancroak

Simple Ruby on Rails authorization

The following describes a simple approach to Ruby on Rails authorization that re-uses the domain model to do the heavy lifting.

Routes:

resources :accounts, only: [:new, :create, :show]

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.

I prefer flat routes (and no subdomains) when at all possible. It keeps the mental overhead low everywhere in the app.

Authentication

Users are authenticated using Clearance. They have a account_id foreign key.

With an authenticated user in a typical “account” application, we can lean on Clearance’s :authorize before filter and ActiveRecord finders.

class BrandsController < ApplicationController
  before_filter :authorize

  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

With this pattern, the user is restricted to interacting with brands to which they have access through their account.

Test at the controller level

it 'does not find brands not associated with user' do
  brand = create(:brand)
  sign_in_as create(:user)

  assert_raises(ActiveRecord::RecordNotFound) do
    get :new, brand_id: brand.to_param
  end
end

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.

Let’s get to green:

class OffersController < ApplicationController
  before_filter :authorize

  def new
    @brand = current_user.brands.find(params[:brand_id])
    @offer = @brand.offers.build
  end
end

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 will make my life easier when the rules around users’ relationship to brands get more complex.

Too lightweight?

This authorization approach requires few lines of code and no extra gem dependencies beyond Rails and Clearance. 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.

Written by .

dancroak

Did you order the code red?!

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.

The problem ~ where does the authorization logic go?

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!
Tom Cruise in a Few Good Men

The solution ~ the non-User model authorizes the User

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.

Yawkey Way

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.