API authentication with devise_token_auth

This post is part of an ongoing feature about creating a social network in Rails.

Any modern Rails web app will need an API to power mobile and/or desktop clients, and maybe even a SPA later on. The devise_token_auth gem adds API user authentication to our app with minimum effort and total configurability. Here's a quick guide on how to set it up, together with solutions to the gotchas that come with it.
We start on our Gemfile, as usual:
gem 'devise'
gem 'devise_token_auth' # Token based authentication for Rails JSON APIs
gem 'omniauth' # required for devise_token_auth

Now, let's generate the User model that will use token authentication. "User" is our model's name, while "auth" is the path where our authentication endpoints will be mounted.
rails g devise_token_auth:install User auth
This command will give us the following:
  • A user model with the proper devise modules for authentication, registration and so on, including the DeviseTokenAuth module for API authentication:
    class User < ActiveRecord::Base
      devise :database_authenticatable, :registerable,
              :recoverable, :rememberable, :trackable, :validatable,
              :omniauthable
      include DeviseTokenAuth::Concerns::User
    end
        
  • A migration to setup the model:
    class DeviseTokenAuthCreateUsers < ActiveRecord::Migration
      def change
        create_table(:users) do |t|
          ## Required
          t.string :provider, :null => false
          t.string :uid, :null => false, :default => ""
    
          ## Database authenticatable
          t.string :encrypted_password, :null => false, :default => ""
    
          # ...
          ## User Info
          t.string :name
          t.string :nickname
          t.string :image
          t.string :email
    
          ## Tokens
          t.text :tokens
    
          t.timestamps
        end
    
        add_index :restaurant_users, :email
        # ...
      end
    end
        
  • The routes for our auth endpoint (I namespaced them to add versioning for the API):
    namespace :api do
      scope :v1 do
        mount_devise_token_auth_for "User", at: 'auth'
      end
    end
        
Finally, the reason why I wrote this blog post —the gotchas.
  • A user that uses DeviseTokenAuth can also be authenticated with normal Devise, but the Devise routes must come first. More info here.
  • Rails comes with CSRF prevention by raising an exception. This will make your token auth fail when accessed via the API. To fix it, change it on your app/controllers/application_controller.rb:
    class ApplicationController < ActionController::Base
      protect_from_forgery with: :null_session
      # ...
    end
        
  • This gem comes with a sturdy security default configuration. Unless you're using the ng-token-auth lib for authentication on Angular, I suggest to disable the change_headers_on_each_reques option on the gem's config (config/initializers/devise_token_auth.rb), to ease development.
With this minimum investment, we get a battle-tested, full-fledged authentication solution. Here are the main endpoints our User auth API will have:
path method purpose
/ POST Email registration. Accepts email, password, and password_confirmation params.
/ DELETE Account deletion. This route destroys users identified by their uid and auth_token headers.
/ PUT Account updates. This route updates an existing user's account settings. The default accepted params are password and password_confirmation.
/sign_in POST Email authentication. Accepts email and password as params. Return a JSON representation of the User model on successful login.
/sign_out DELETE Use this route to end the user's current session, by invalidating their authentication token.
/validate_token GET Use this route to validate tokens on return visits to the client. Accepts uid and auth_token as params.
/password POST Use this route to send a password reset confirmation email to users that registered by email.
/password PUT Use this route to change users' passwords.
/password/edit GET Verify user by password reset token. This route is the destination URL for password reset confirmation.

Have you tried any other token-based solution for API authentication, or built one yourself? Having problems implementing this one on your app? Share it on the comments!

1 comment:

  1. Fantastic post, very informative. I wonder why the other specialists of this sector do not notice this. You must continue your writing. I'm confident, you have a great readers' base already!
    Social Network Web Development

    ReplyDelete