Adding Token Based Authentication To a Rails API with Devise Token Auth

Bootstrapping an API these days is simple as running a single command to generate the project. Starting from there you can add some models, some controllers, and you are (almost) ready to go.

Rails for instance supports the –api flag since the 5.0 version which can be used for creating a stripped Rails application containing only the bare minimum for serving API resources.

But how about authentication? Even for a simple side project you will need a basic form of protection for your API endpoints.

Devise is the way to go when it comes to Rails, however it could be overkill for an API-only application, not to mention that the Gem should be tweaked a bit in order to make it working.

So what is the most straightforward way to implement authentication for a Rails 5 API? The answer could probably be devise_token_auth.

I took some time to explore the Gem and I’m ready to show you how to implement the authentication inside a simple Rails API-only application.

What you will learn

1. How to add token based authentication to a Rails API with Devise Token Auth

Disclaimer

Authentication and authorization are difficult: this guide provides a basic example which does not cover every possible use cases but will rather give you a good starting point.

Also, keep in mind that the code in this tutorial is not suitable for production. Use it at your own risk. Its purpose is to show you the basics. Don’t copy/paste the examples without having a solid understanding about what the code is supposed to do.

Requirements

In order to follow along with this tutorial you should have Rails installed on your system. I suggest using Rbenv to set up an environment: go to https://github.com/rbenv/rbenv and follow the instructions.

Another option would be the good ol’ RVM. Pick whichever tool you prefer.

Configuration and scaffolding

To start with, we should initialize our Rails API. Inside the terminal move to a directory of your choice and generate the project:

rails new rails-api-auth-example --api

The –api flag tells Rails to get rid of everything but the minimum, by limiting the set of middlewares included inside the application and by avoiding the generation of views and other assets.

Setting up the Routes

Declaring routes in Rails is deadly easy. All you need to do is define a resource inside the routes configuration file. A resource in Rails represents a collection of objects: for instance if you want to build a blog you’ll likely need an articles resource. Since our API will store a collection of articles it should be also able to provide the user with some routes for showing, adding, and destroying said articles.

Open up config/routes.rband add your first resource:

Rails.application.routes.draw do
  resources :articles
end

now type in your terminal:

rails routes

and you will be able to see how Rails has automagically generated all the routes for you:

      Prefix Verb   URI Pattern                  Controller#Action
    products GET    /articles(.:format)          articles#index
             POST   /articles(.:format)          articles#create
 new_product GET    /articles/new(.:format)      articles#new
edit_product GET    /articles/:id/edit(.:format) articles#edit
     product GET    /articles/:id(.:format)      articles#show
             PATCH  /articles/:id(.:format)      articles#update
             PUT    /articles/:id(.:format)      articles#update
             DELETE /articles/:id(.:format)      articles#destroy

Generating the Model

Now that we have our routes and our articles resource, we need to create a model. To keep things simple, our article will have only two attributes, a title and a body:

rails generate model Article title:text body:text

Rails will create the Article model alongside the corresponding migration.

Don’t forget to commit your migration with:

rails db:migrate

Adding the Authentication with Devise Token Auth

Most of the times in Rails you may want to generate a controller right after creating the model. But before going further it’s time to add the authentication layer to our API: you may want to protect the creation of new articles as well as the deletion of the existing ones.

Devise Token Auth makes the process extremely simple.

In order to use the Gem you need to update your Gemfile by adding:

gem 'devise_token_auth'

Then update the dependencies with:

bundle install

Devise Token Auth can be configured either manually by modifying the configuration files or by using a handy generator to do all the heavy lifting. I strongly recommend the second approach. To start with, we need to create the User model:

rails generate devise_token_auth:install User auth

If you snitch inside app/models/user.rb you’ll see that Devise Token Auth added some default modules: you are free to configure the model (and the corresponding migration) to suits your needs.

For example you can remove :omniauthable if you don’t plan to use Omniauth, or you can also remove :confirmable if you don’t want to send an email confirmation to the user.

At the end, a basic User model will look like the following:

class User < ActiveRecord::Base
  # Include default devise modules.
  devise :database_authenticatable, :registerable,
          :recoverable, :rememberable, :trackable, :validatable
  include DeviseTokenAuth::Concerns::User
end

The User model is good to go but first another important step before moving on: when using an API-only application, you must tell Devise to not use the ActionDispatch::Flash middleware.

Create a configuration file for Devise in config/initializers/devise.rb and add:

Devise.setup do |config|
    config.navigational_formats = [ :json ]
end

When ready, commit again the migrations:

rails db:migrate

By taking a look at the routes now you’ll see how Devise Token Auth will add all the new authentication/authorization endpoints. Type:

rails routes

and you will see a lot of new routes among the existing ones:

                  Prefix Verb   URI Pattern                    Controller#Action
        new_user_session GET    /auth/sign_in(.:format)        devise_token_auth/sessions#new
            user_session POST   /auth/sign_in(.:format)        devise_token_auth/sessions#create
    destroy_user_session DELETE /auth/sign_out(.:format)       devise_token_auth/sessions#destroy
       new_user_password GET    /auth/password/new(.:format)   devise_token_auth/passwords#new
      edit_user_password GET    /auth/password/edit(.:format)  devise_token_auth/passwords#edit
           user_password PATCH  /auth/password(.:format)       devise_token_auth/passwords#update
                         PUT    /auth/password(.:format)       devise_token_auth/passwords#update
                         POST   /auth/password(.:format)       devise_token_auth/passwords#create
cancel_user_registration GET    /auth/cancel(.:format)         devise_token_auth/registrations#cancel
   new_user_registration GET    /auth/sign_up(.:format)        devise_token_auth/registrations#new
  edit_user_registration GET    /auth/edit(.:format)           devise_token_auth/registrations#edit
       user_registration PATCH  /auth(.:format)                devise_token_auth/registrations#update
                         PUT    /auth(.:format)                devise_token_auth/registrations#update
                         DELETE /auth(.:format)                devise_token_auth/registrations#destroy
                         POST   /auth(.:format)                devise_token_auth/registrations#create
     auth_validate_token GET    /auth/validate_token(.:format) devise_token_auth/token_validations#validate_token
                articles GET    /articles(.:format)            articles#index
                         POST   /articles(.:format)            articles#create
                 article GET    /articles/:id(.:format)        articles#show
                         PATCH  /articles/:id(.:format)        articles#update
                         PUT    /articles/:id(.:format)        articles#update
                         DELETE /articles/:id(.:format)        articles#destroy

Generating a Controller

One last piece is missing: a controller for our article model.

If you take a look again at the above output you’ll see that the user should visit the /articles endpoint in order to see all our articles.

Plus, by looking at the Controller#Action column, you can guess that Rails expects a controller named articles to be present, alongside a corresponding action named index.

Also, to create a new article the user could make a POST request to /articles: that means a corresponding action named create should be defined as well inside the controller.

The index method of the controller should publicy display all our items thus there is no need to authenticate the user. While it’s fine to make all our articles available for displaying, only a registered user should be able to create a new article. By using Devise Token Auth you can lock down all the controller’s actions, except index for example.

Let’s create the controller with:

rails generate controller Articles

Now, let’s get back to our actions: index and create. The create action will be protected by Devise Token Auth. Open up app/controllers/articles_controller.rb and add the following code:

class ArticlesController < ApplicationController
    before_action :authenticate_user!, except: [ :index ]

    def index
        @articles = Article.all()
        render json: @articles
    end

    def create
        article = Article.create(
            title: params[:title],
            body: params[:body]
            )
        render json: { article: article }
    end
end

Notice the before_action call followed by :authenticate_user!. If you did some work with Rails you should already knew that before_action is a filter: it gets executed before a controller action.

:authenticate_user! on the other hand is an helper provided by Devise: it returns a 401 error unless the User is signed-in.

At this point we have everything in place, authentication included. Our basic API is ready.

Interacting with the Rails API

Our API is good to go and we can start interacting with it.

First, start Puma with:

rails server

Then, if you haven’t already, go ahead and install Postman.

To make your life easier I’ve made available a Postman Collection with some examples. Download the JSON file and import the collection inside Postman. Right after importing the file you’ll see the newly rails-api-auth-example collection available in Postman:

Devise Token Auth: Postman Collection for rails-api-auth-example
The rails-api-auth-example collection

If you go straight over to “CREATE: Sample article” you’ll get an error: “You need to sign in or sign up before continuing”. Devise Token Auth complains because we’re not a registered user:

Devise Token Auth: Rails API 401
Only a registered user will be able to create a new article

fear not, the issue can be solved by creating a new API user.

Creating a new API user

POSTMAN: select the request named “CREATE: New user“. You can tweak the body by changing the username and the password. Since it’s just a development environment, pick whichever credentials you prefer.

WARNING!! In the example I’m sending an username and a password in plain text inside the POST request. Again, this is just a development environment and we will be fine here, but in production you must make sure that the API will listen only over SSL. Also, do not never ever store credentials in plain text!

Clic the Send button when you’re ready and take a look at the Headers section of the response. You’ll see some interesting headers among the others:

access-token, client, uid, and expiry.

Devise Token Auth create user
Devise Token Auth response headers

it’s Devise Token Auth again doing the magic. A new API user has been created! You can use the token, the client, the uid, and expiry to make an authenticated request to the API. Do you remember how we’ve locked down the create action inside the Article controller? A user won’t be able to create a new article unless it is registered and authorized within the API.

Creating a new article

POSTMAN: select the request named “CREATE: Sample article“. You should update the request Headers to match all the response Headers you saw during the creation of the new user: access-token, client, uid, and expiry.

When you’re done setting up the request Headers, clic the Send button and take a look at the response: you should see the newly created article!

Devise Token Auth: creating an article as a registered user
A registered user would be able to create a new article inside our API

Also, take a look at the response Headers. You’ll notice how the access-token has changed: the token changes upon every request and it’s up to the client to  keep track of the new value over time. If you want to send out another POST request, you must make sure that the access-token request header matches the value sent by the server in the latest response.

At this point you’re ready to start exploring all the functionalities offered by Devise Token Auth. Even if the API is still really basic it took only few lines of code to implement a simple authentication system. I think it’s a fantastic starting point.

Check out the other examples inside the Postman Collection. There is a request for displaying all the articles, which will work out of the box (every user can see the articles list) and other stuff like the Sign in and the Sign out requests.

The code

The code for the project is available on Github: rails-api-auth-example

The Postman collection with all the examples is available on Github:
Postman Collection

Where to go from here

Authentication and authorization are difficult and the above tutorial is not perfect by any means. Did you find some errors in my code? I’ll be glad to hear your voice in the comments below. Is there something better than Devise Token Auth? Probably yes. Again, feel free to share your views.

Frontend: our API works fine with Postman for now but it should be paired with a frontend sooner or later. Pick React, Vue.Js or something else: it’s a matter of personal preferences. The logic for managing and storing the token will be the main challenge here.

Deploying in production: working in a development environment is relatively easy. Try to deploy the project in production by starting to implement SSL to securely exchange data with a client.

JWT: JSON Web Token is another emerging tecnology for managing the authentication process inside API applications and there are some great Gems available. Anyway, make sure to read JSON Web Token is a bad stardard first.

Extending the API. try to extend the API by supporting the POST, PUT, and DELETE operations.

Adding some tests: the API lacks of tests. After adding your new actions inside the controller, try to write some tests

Thanks for reading!

Valentino Gagliardi

Valentino Gagliardi

Web Developer & IT Consultant, with over 10 year of experience, I'm here to help you developing your next idea.
Valentino Gagliardi

14 Replies to “Adding Token Based Authentication To a Rails API with Devise Token Auth”

  1. devise_token_auth is a buggy gem. I recommend people avoid it. It’s come up in the ActiveModelSerializer’s issue queue a few times and was quite difficult to work with. My reading is that the source code is too complex given its very simple job. See https://gist.github.com/josevalim/fb706b1e933ef01e4fb6#file-2_safe_token_authentication-rb which references original implementation which was removed from devise as well as a suggested alternative, to use a token and identifier.

    Re; JWT, agree to avoid for auth. There’s no advantage to it over a meaningless token under most use cases, but incurs computational cost. e.g. see https://github.com/shieldfy/API-Security-Checklist/issues/6#issuecomment-317308169

  2. Hi, what about the omniauth addition to it. It’s not working that great with devise_token_auth and rails-api.

    Whetherbyou tried that with the rails API.

  3. Is “devise” gem a requirement to implement this tutorial? I already have a ‘User’ model, so it’s safe to run this line? ‘rails generate devise_token_auth:install User auth’

  4. Hi I am just a beginner and I am having some issues with postman, I want to add the devise authentication token for my own database, but then when I open postman, I am confused as to what the file is that we have to import and why. And, how to do those things to my own database. I’m sorry if this is a stupid question, but I was hoping someone could explain to me what is going on.

  5. Can confirm devise_token_auth can be a nightmare to work with. But what are the alternatives?!

    Is it time to just reimplement a token-based devise again?

    Not having a great token library for Rails API is an existential threat to Ruby.

  6. Thank you for sharing. In the very beginning you tell to add article resource, but you display products one with `rails routes` command, just a typo ?

  7. Really nice tutorial,my problem is trying to add associations belongs_to and has_many to the app,user has many articles and articles belongs to user, could you please point me to a resource where i can learn that please.

Leave a Reply

Your email address will not be published. Required fields are marked *