How to skip Devise authentication when using an API key?
Solution 1
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.rb
and are accessible via routes such as /api/v1/somethings
. Each of the specific API controllers inherits from a base API controller that inherits from ActionController::Base
, so does not include any of the filters defined on ApplicationController
.
Solution 2
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.
Solution 3
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.
Solution 4
An alternative to Gazler's would be to use an except:
class ApplicationController < ActionController::Base
before_filter :authenticate_user!, except: :some_json_method
def some_json_method
render :nothing unless params[:api_key] == '1234'
render :json
end
end
This way you don't open your entire app to the key-holder (depending on your needs, whether you need that or not). If you need multiple methods opend to the key, you could probably also use something like:
class ApplicationController < ActionController::Base
JSON_METHODS = [method_1, method2]
before_filter :authenticate_user!, except: JSON_METHODS
before_filter :authenticate_token, only: JSON_METHODS
private
def authenticate_token
params[:api_key] == '1234'
end
end
FilmiHero
Updated on June 05, 2022Comments
-
FilmiHero almost 2 years
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 2Whats 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 thebefore_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 ofApplicationController
so that it bypassed Devise'sbefore_filter :authenticate_user!
code within theApplicationController
.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 ...