Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to skip Devise authentication when using an API key?

I'm using Devise on my application and would like to create a global API key that can access JSON data of anyone's account without having to log-in.

For example, say my API Key is 1234 and I have two users who have created two different restaurants.

  • User 1 - Restaurant 1 (/restaurants/1)
  • User 2 - Restaurant 2 (/restaurants/2)

And I open a brand new browser and haven't logged into anything and I pass into my URL .../restaurants/2.json?api_key=1234, I should be able to access the JSON data of that restaurant without having to log-in as User 2

Whats the best way to do this?

I've followed the Railscast #352 Securing an API so I'm able to access JSON stuff by passing in the API key but I have to log-in to see anything.

Edit 1: Using CanCan

I should mention that I'm also using CanCan for roles but not sure if that'll play any role (pun not intended) in this situation.

Edit 2: Implimenting with API Versioning

I followed the Railscast #350 and #352 which teach you how to create REST API Versioning and how to secure it with an API Key.

Here's what my controllers/api/v1/restaurants/restaurants_controller.rb looks like:

module Api
  module V1
    class RestaurantsController < ApplicationController
      before_filter :restrict_access

      respond_to :json

      def index
        respond_with Restaurant.all
      end

      def show
        respond_with Restaurant.find(params[:id])
      end

    private

      def restrict_access
        api_key = ApiKey.find_by_access_token(params[:api_key])
        head :unauthorized unless api_key        
      end
    end
  end
end

And my application_controller.rb still has the before_filter :authenticate_user! code in it.

Solution

I first followed the Railscast #350 on REST API Versioning and moved all my JSON API calls to /apps/api/v1/...

Then, following Steve Jorgensen's solution below, made sure my API module inherited from ActionController::Base instead of ApplicationController so that it bypassed Devise's before_filter :authenticate_user! code within the ApplicationController.

So, my Edit 2 code when from looking like this:

module Api
  module V1
    class RestaurantsController < ApplicationController
    ...

to

module Api
  module V1
    #Replace 'ApplicationController' with 'ActionController::Base'
    class RestaurantsController < ActionController::Base
    ...
like image 523
FilmiHero Avatar asked Aug 02 '12 19:08

FilmiHero


2 Answers

I know its been a while since this was asked, but thought I would throw in one more option that i've used in the past

class Api::ApplicationController < ApplicationController

skip_before_filter :authenticate_user!

This assumes that you have an application controller in your API directory that all of your api controllers inherit from. If not, you can just put the skip in each controller.

like image 58
Sean Avatar answered Oct 05 '22 23:10

Sean


You can do this with your before_filter in your Controller.

Currently, you probably have something like:

class SomeController < ApplicationController
  before_filter :authenticate_user!
end

Instead of calling this, you can define a different method (ideally in ApplicationController)

class ApplicationController < ActionController::Base
  before_filter :authenticate_or_token

  private
  def authenticate_or_token
    if params[:api_key] == 1234
      @current_user = User.new(:admin => true, :any => "other", :required => "fields")
      return current_user
    end
    authenticate_user!
  end

I would recommend using a more robust method of authentication such as OAuth, but this should work for a simple 1-key based authentication.

like image 34
Gazler Avatar answered Oct 06 '22 01:10

Gazler