public
Last active

Authenticate your API with devise, token by header

  • Download Gist
gistfile1.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
#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"})

With an API REST like this you can map external resource in another rails app.

#In another rails app
class Product < ActiveResource::Base
  headers['X-AUTH-TOKEN'] = 'G3xbSrS4uqLU484eUw9h'
  self.site = "http://localhost:3001/api/v1"
end

Np :)

I just added routes.

If you have operations extra rest, for example close:

namespace :api, :defaults => {:format => 'json'} do
    devise_for :users
    namespace :v1 do
      resources :products do
         member do
           get 'close'
         end
      end
    end
  end

then, in your client application, you can use it with active resource model (first comment) in this manner

Product.find(1).get :close

I was looking for having auth token through header. I seems prepend_before_filter did the trick.

Great gist!

Does this approach leave open the possibility of CSRF?
If you use before_filter :authenticate_user!, it will check to see if user is logged in by any possible mean (not only auth_token), and if CSRF token is not checked, another website can get your data, right?

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.