Skip to content

Instantly share code, notes, and snippets.

@marcomd
Created July 17, 2012 12:18
Show Gist options
  • Star 35 You must be signed in to star a gist
  • Fork 15 You must be signed in to fork a gist
  • Save marcomd/3129118 to your computer and use it in GitHub Desktop.
Save marcomd/3129118 to your computer and use it in GitHub Desktop.
Authenticate your API with devise, token by header
#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"})
@marcomd
Copy link
Author

marcomd commented Jul 18, 2012

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

@mateusmaso
Copy link

thanks man

@marcomd
Copy link
Author

marcomd commented Jul 24, 2012

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

@renanra
Copy link

renanra commented Jul 21, 2013

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

Great gist!

@vfonic
Copy link

vfonic commented Aug 14, 2013

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?

@nihsmik
Copy link

nihsmik commented Feb 2, 2015

Thank you! I is very helpful :)

@supavog
Copy link

supavog commented Feb 3, 2017

Any idea why I would be getting "NoMethodError (undefined method 'ensure_authentication_token!' for #User:0x007f3764a07470>):"? Where should this be defined?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment