flonath
12/24/2013 - 7:03 PM

Authenticate your API with Devise gem, token by header. Ruby on Rails 4. Read the comments.

Authenticate your API with Devise gem, token by header. Ruby on Rails 4. Read the comments.

#Session controller provides a token
#/controllers/api/sessions_controller.rb
class Api::SessionsController < Devise::SessionsController
  before_filter :authenticate_user!, :except => [:create]
  before_filter :ensure_params_exist, :except => [:destroy]
  respond_to :json

  def create
    resource = User.find_for_database_authentication(:email => params[:user_login][:email])
    return invalid_login_attempt unless resource

    if resource.valid_password?(params[:user_login][:password])
      sign_in(:user, resource)
      resource.ensure_authentication_token!
      render :json=> {:auth_token=>resource.authentication_token, :email=>resource.email}, :status => :ok
      return
    end
    invalid_login_attempt
  end

  def destroy
    resource = User.find_by_authentication_token(params[:auth_token]||request.headers["X-AUTH-TOKEN"])
    resource.authentication_token = nil
    resource.save
    sign_out(resource_name)
    render :json => {}.to_json, :status => :ok
  end

  protected
  def ensure_params_exist
    return unless params[:user_login].blank?
    render :json=>{:message=>"missing user_login parameter"}, :status=>422
  end

  def invalid_login_attempt
    render :json=> {:message=>"Error with your login or password"}, :status=>401
  end
end

#Base controller which inherited by every api controller 
#/controllers/api/base_controller.rb
class Api::BaseController < InheritedResources::Base
  before_filter :authenticate_user!
  prepend_before_filter :get_auth_token

  respond_to :xml, :json

  private
  def get_auth_token
    if auth_token = params[:auth_token].blank? && request.headers["X-AUTH-TOKEN"]
      params[:auth_token] = auth_token
    end
  end
end

#Your resources controllers
#/controllers/api/v1/products_controller.rb
module Api
  module V1
    class ProductsController < Api::BaseController
    end
  end
end

#Routes
  namespace :api, :defaults => {:format => 'json'} do
    devise_for :users
    namespace :v1 do
      resources :products
    end
  end

#*** How to get a token: well, you have to provide email and password ***
#with curl
curl -d "user_login[email]=your@email.com&user_login[password]=yourpassword" http://localhost:3001/api/users/sign_in --noproxy localhost
{"email":"your@email.com","success":true,"auth_token":"G3xbSrS4uqLU484eUw9h"}

#with HTTPClient
clnt = HTTPClient.new
res = clnt.post("http://localhost:3001/api/users/sign_in", {"user_login[email]" => "your@email.com", "user_login[password]" => "yourpassword"})
auth_token = JSON(res.body)["auth_token"]
=> "G3xbSrS4uqLU484eUw9h"


#*** How to get data using the token ***
#with curl
curl -H "X-AUTH-TOKEN: G3xbSrS4uqLU484eUw9h" http://localhost:3001/api/v1/products/1 --noproxy localhost --get

#with HTTPClient gem
clnt = HTTPClient.new
clnt.get("http://localhost:3001/api/v1/products/1", nil, {"X-AUTH-TOKEN" => "G3xbSrS4uqLU484eUw9h"}).content


#*** At the end, remove the access token ***
#with curl
curl -X DELETE -H "X-AUTH-TOKEN: G3xbSrS4uqLU484eUw9h" http://localhost:3001/api/users/sign_out

#with HTTPClient gem
clnt.delete("http://localhost:3001/api/users/sign_out", {"X-AUTH-TOKEN" => "G3xbSrS4uqLU484eUw9h"})