---
title: Token Authentication with Rails
teaser: 'How can you do token authentication with Rails out of the box?

  '
tags: rails,api
author: Nick Charlton
published_on: 2019-11-08
---

Sometimes you don't need a complex authentication approach for a project. Let's
say that you're working on the first iteration of a mobile app with a
Rails-based API. You need to authenticate a user, but you don't want to
implement something like [OAuth 2][].

Rails has this built-in, but it doesn't seem to be that well known. We've never
written about it, and I've found myself finding it very useful again recently.

It's called [`authenticate_or_request_with_http_token`][api].

## Implementation

```ruby
module Api
  class BaseController < ApplicationController
    before_action :authenticate

    private

    def authenticate
      authenticate_or_request_with_http_token do |token, _options|
        User.find_by(token: token)
      end
    end

    def current_user
      @current_user ||= authenticate
    end
  end
end
```

On every request, we take out the token then try and find a user
associated with it.

Unfortunately, this approach exposes you to a [timing attack][]. There's not a
good, general solution to this problem so you should think about it before
implementing something similar.

In fitting with the typical pattern of implementing `current_user`, we do the
same here too.

As an example, we might talk to an API implemented this way with `curl` like
this:

```sh
curl -H "Authorization: Token abc123" http://localhost:3000/api/test
```

## Testing

We might go about testing this like so:

```ruby
require "rails_helper"

RSpec.describe "Authenticating with the API" do
  before do
    Rails.application.routes.draw do
      get "/api/test" => "test#index"
    end
  end

  after do
    Rails.application.reload_routes!
  end

  context "when the user provides a valid api token" do
    it "allows the user to pass" do
      create(:user, token: "sekkrit")
      credentials = authenticate_with_token("sekkrit")

      get "/api/test", headers: { "Authorization" => credentials }

      expect(response).to be_successful
      expect(response.body).to eq({ "message" => "Hello world!" }.to_json)
    end
  end

  context "when the user provides an invalid api token" do
    it "does not allow to user to pass" do
      create(:user, token: "sekkrit")
      credentials = authenticate_with_token("less-sekkrit")

      get "/api/test", headers: { "Authorization" => credentials }

      expect(response).to be_unauthorized
    end
  end

  private

  TestController = Class.new(Api::BaseController) do
    def index
      render json: { message: "Hello world!" }
    end
  end

  def authenticate_with_token(token)
    ActionController::HttpAuthentication::Token.encode_credentials(token)
  end
end
```

To isolate our test here from the rest of our application, we're drawing a
route just for this example. Then we can test both successful and unsuccessful
responses as a [`request` spec][rs].

[There's also an example Rails project on GitHub which shows this
action][example].

[OAuth 2]: https://tools.ietf.org/html/rfc6749
[api]: https://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html
[timing attack]: https://github.com/thoughtbot/guides/blob/master/security/application.md#timing-attacks
[rs]: https://relishapp.com/rspec/rspec-rails/v/3-9/docs/request-specs/request-spec
[example]: https://github.com/thoughtbot/rails-token-authentication-example
