---
title: Forbidden Kisses &amp; HTTP Fluency in Clearance
teaser:
tags: news,web,rails,clearance
author: Dan Croak
published_on: 2009-02-22
---

**UPDATE:** _After about two years of using this approach in Clearance, we
[removed the 403 Forbidden
feature](https://github.com/thoughtbot/clearance/commit/b004f199acb1e11672e78de8a8775b2979c4f958)
in Clearance. We discovered that setting the 403 status code turned out to be a
bad user experience in some browsers such as Chrome on Windows machines.
Philosophically, we decided we value user experience over technical purity._

[Clearance](https://thoughtbot.com/blog/clearance-rails-authentication-with-email-and-password)
tries to be fluent in HTTP. That means a few things:

* Know when to return which HTTP status codes.
* Know when to raise errors.

## 401 Unauthorized

In layman's terms:

> Specifically for use when authentication is possible but has failed or not yet
> been provided.

The response is [401
Unauthorized](http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_Error)
out of the box with Clearance when:

* A user tries to sign in with bad credentials.
* A user without confirmed email tries to sign in.

If you protect an action with `before_filter :authenticate` in your app,
Clearance will also return 401 Unauthorized when:

* A user who is not signed in tries to access that action.

## 403 Forbidden

In layman's terms:

> The request was a legal request, but the server is refusing to respond to it.
> Unlike a 401 Unauthorized response, authenticating will make no difference.

![''](http://images.thoughtbot.com/ui/2009-2-22-forbdden_kiss.jpg)

The response is [403 Forbidden](http://en.wikipedia.org/wiki/HTTP_403) out of
the box with Clearance when:

* A user tries to confirm a user with confirmed email.
* A user tries to confirm a user without a token.
* A user tries to confirm a user without the correct token for an unconfirmed
  user.
* A user tries to edit a user's password without a token.
* A user tries to update a user's password without a token.
* A user tries to edit a user's password without the correct token for the user.
* A user tries to update a user's password without the correct token for the
  user.

These are legal requests by someone or something (maybe a malicious user)
requesting actions in forbidden, exceptional ways. They are not available to any
user, regardless of their authentication status. The server should refuse to
respond to it.

## When to raise errors

Consider a typical edit, show, or destroy action:

    def show
      @user = User.find(params[:id])
    end

In the development and test environments, this will raise a
`ActiveRecord::RecordNotFound` error if a User does not exist for the given id.
In production, this will return 404 Not Found instead of 500 Internal Server
Error.

Rails does this by rescuing the `ActiveRecord::RecordNotFound` error for
**public requests** (for example, staging or production environments). Inside
the rescue, it returns the logical status code, `:not_found`. For [**local
requests**](http://api.rubyonrails.org/classes/ActionController/Rescue.html#M000397)
(for example, development or test environments), the error is not rescued.

Rails provides similar functionality for other errors:

    'ActionController::RoutingError'             => :not_found,
    'ActionController::UnknownAction'            => :not_found,
    'ActiveRecord::RecordNotFound'               => :not_found,
    'ActiveRecord::StaleObjectError'             => :conflict,
    'ActiveRecord::RecordInvalid'                => :unprocessable_entity,
    'ActiveRecord::RecordNotSaved'               => :unprocessable_entity,
    'ActionController::MethodNotAllowed'         => :method_not_allowed,
    'ActionController::NotImplemented'           => :not_implemented,
    'ActionController::InvalidAuthenticityToken' => :unprocessable_entity

This maps errors to HTTP status codes.

Clearance creates a custom error, `ActionController::Forbidden`, and maps it to
`:forbidden` to match this convention

So when situations arise when 403 Forbidden is called for, Clearance simply
does:

    raise ActionController::Forbidden

The effect is exactly like `ActiveRecord::RecordNotFound`. In development and
test environments, the developer has the opportunity to investigate what is
going wrong. In staging and production, the app behaves like a good internet
citizen by responding with the correct HTTP status code.

_Note:_ One could argue that Rails could provide a mapping like this for all
HTTP status codes, or at least a few more of the most common ones. A patch for
another day, perhaps.

## Attribution

These ideas were lifted from the good work coming out of the
[Merb](http://merbivore.com) community. The implementation was driven out
through conversations with Tim Pope, Joe Ferris, Mike Burns, and Jason Morrison.

[Clearance is on GitHub](http://github.com/thoughtbot/clearance).
