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
...
One option no one has mentioned is to have a completely separate set of controllers for the API that do not inherit from
ApplicationController.I have seen the pattern used where API controllers live in files such as
/app/controllers/api/v1/somethings.rband are accessible via routes such as/api/v1/somethings. Each of the specific API controllers inherits from a base API controller that inherits fromActionController::Base, so does not include any of the filters defined onApplicationController.