giant robots smashing into other giant robots

Written by thoughtbot

dancroak

International authentication library of mystery

In May, Timur Vafin, Marcel Goerner, and Eugene Bolshakov helped make Clearance i18n-aware.

Baxter, you know I don’t speak Spanish

Even if your language is English, this means you can control all of Clearance’s flash keys and email subjects from one YAML file.

Pretty sweet! Thanks for the patches, fellas.

en:
  flashes:
    failure_when_forbidden:
      'Please double check the URL or try submitting the form again.'
    failure_after_update:
      "Password can't be blank."
    failure_after_create:
      'Bad email or password. Are you trying to register a new account?
       <a href="%{sign_up_path}">Sign up</a>.'
  helpers:
    submit:
      password:
        submit:
          'Reset password'
      password_reset:
        submit:
          'Save this password'
      session:
        submit:
          'Sign in'
      user:
        create:
          'Sign up'
    label:
      password:
        email:
          'Email address'
      password_reset:
        password:
          'Choose password'
  layouts:
    application:
      sign_in:
        'Sign in'
      sign_out:
        'Sign out'
  passwords:
    create:
      description:
        'You will receive an email within the next few minutes.
         It contains instructions for changing your password.'
    edit:
      title:
        'Change your password'
      description:
        'Your password has been reset. Choose a new password below.'
    new:
      title:
        'Reset your password'
      description:
        'To be emailed a link to reset your password, please enter your email
         address.'
  sessions:
    new:
      title:
        'Sign in'
      sign_up:
        'Sign up'
      forgot_password:
        'Forgot password?'
  users:
    new:
      sign_in:
        'Sign in'
  clearance_mailer:
    change_password:
      beginning_paragraph:
        'Someone, hopefully you, requested we send you a link to change your
         password:'
      ending_paragraph:
        "If you didn't request this, ignore this email. Your password hasn't
         been changed."

Written by .

dancroak

Sign up, sign in, sign out

What is your Ubiquitous Language for authentication?

Common language includes “logging in”, “registering”, “joining up”, “creating an account”, “signing up”, and “signing out.”

Do you interchange these words in your UI? How about in your application code? Tests? Does your whole team use the same language across all layers of the software?

When writing a library like Clearance, I feel the authors should take a stand on language to alleviate the mental burden on designers, developers, and business analysts involved in the project.

The decision is basically arbitrary. No one set of terms is better than the other. What’s important is to take the opportunity to standardize all the places this shows up in code.

So we picked “sign up”, “sign in”, and “sign out”, a lingual trinity of authentication.

We liked the symmetry. We liked that there was less of a chance of writing “log in” (sounds like a verb) or “log_in” in certain places and “login” (sounds like a noun) other places.

About a half a year later and we still feel good about the effect.

Tests

context 'when signed out' do
  context 'on get to new' do
    setup do
      get :new
    end

    should_deny_access
  end
end

context 'when signed in' do
  setup do
    sign_in
  end

  context 'on get to new' do
    setup do
      get :new
    end

    ...
  end
end

context 'when signed in as an admin' do
  setup do
    sign_in_as create(:admin)
  end

  context 'on delete to destroy' do
    setup do
      delete :destroy
    end

    ...
  end
end

Application code

class UsersController < Clearance::UsersController
  def create
    ...
    sign_in(@user)
  end
end

class ParticipationsController < ApplicationController
  private

  def ensure_signed_in
    unless signed_in?
      store_location
      flash[:failure] = 'Please sign in to participate in this deal.'
      redirect_to sign_in_path
    end
  end
end

module NavigationHelper
  def authentication_links
    if signed_in?
      signed_in_links
    else
      signed_out_links
    end
  end

  def signed_out_links
    [
      link_to('Sign up', sign_up_path),
      link_to('Sign in', sign_in_path)
    ]
  end

  def signed_in_links
    [
      link_to('My Account', edit_user_path(current_user)),
      link_to('Sign out', sign_out_path)
    ]
  end
end

UI

For something as common as authentication, familiarity for users breeds comfort. So what do the others do?

Basecamp is a little inconsistent, mixing sign in with login:

Apple is pretty as usual:

Google is perfectly consistent. Youtube and Gmail:

Sweet, sweet consistency.

Written by .

dancroak

Clearance: Rails authentication with email and password

imageAuthentication is a common pattern in Rails apps. Thus, there have been many authentication plugins. We’ve tried acts_as_authenticated and restful_authentication over the years.

We found that user authentication is hard to generalize. Most abstracted authentication plugins had both too much and too little for us.

We then tried writing authentication from scratch on our clients’ Rails apps for about a year. We felt better about test coverage but it was still a pain to re-write similar code. App after app, we talked about extracting common code into a library. Each time we resisted.

After a while, we thought that maybe 60% of authentication could be re-used. We extracted Hoptoad’s authentication, then merged code from two of our clients’ apps. We named the gem Clearance.

On the first attempt, we went overboard on re-use. We backed off and wrote hooks in places we were finding logical extension points. For the past few months, patches have trickled in from Github and we’ve carefully included code that fits in the “60%”.

We recently started a new project. In the process, we’ve polished the gem and are happy to announce its official release.

Clearance

  • Sign up
  • Confirm email
  • Sign in
  • Sign out
  • Reset password

Get it on Github.

Modules, Shoulda, and Factory Girl

Clearance is focused on maintainability of your application’s authentication code.

  • Include comprehensive Shoulda and Factory Girl tests in your Rails app’s test suite
  • Encapsulate authentication logic in modules which are included in your controllers, models, and tests.

This approach keeps your Rails application’s code clean and alerts you if you ever break your authentication code.

Due to the work we’ve been doing to make Shoulda test framework-agnostic, you will be able to use RSpec in the 0.5.0 release of Clearance.

Test::Unit and Cucumber features are also supported:

script/generate clearance
script/generate clearance_features

Conventions

To keep our approach simple, we made a series of design decisions:

  • User model required.
  • User model uses attr_accessible.
  • Authenticate by email (not username) and password.
  • Vocabulary restricted to a trinity: “sign up”, “sign in”, “sign out”

Beyond

Clearance does not try to be a Swiss Army knife but it does have some hooks if you want admin roles, sign up and sign in by username in addition to email, or something else.

Please report bugs and request features on GitHub Issues.

Written by .