I would like to use my RoR application to handle both UI and ReSTful API requests, however
I'm running into problems because of the protect_from_forgery flag. I don't want the API user to have to store a cookie or have to pass an authenticity_token; and, I want to be able to use the same ReSTful queries for both the UI and the API. For example,
https://host.domain.com/user/show/1
https://host.domain.com/user/create
The application controller would need to check if an API call is being made and then
not call the protect_from_forgery flag. So something like:
skip_before_filter :verify_authenticity_token, :only => :api
But here :api is an action and I would need to map a new route. I don't think this would allow me to use the same controllers and actions as the UI.
So I'm confused and unsure how to approach this problem. Surely this is a common issue
that many others have had to solve. I appreciate any help that folks could provide.
Here's a yucky idea: skip the before filter, for all actions. Then add a new filter
def verify_authenticity_token_or_api
api? || verify_authenticity_token
end
In your api method, work out whether the request is an api request. Be careful to not allow people to bypass forgery protection by making requests that look a bit like api requests - you might want to do something like invalidate the user's session if there is something that looks like a api request.
Another approach is just to have dedicated endpoints and controllers for your apis. While the stuff rails gets you for free is appealing, sometimes the resources you want to expose don't correspond exactly to your models
Related
I am building an API-only (for now) Rails app to serve as the back end for an Android app I'm building. I was previously using Firebase but wanted to do more processing on application data and I didn't want to bloat the mobile client with all the logic, so I am moving away from Firebase's real-time database and backing the application data using Rails. I was also using Firebase's authentication which is very straightforward. But it seems less complex for the overall system to keep all of this functionality in one place, so I'd like to perform auth and user management in the Rails app as well.
I have installed devise_token_auth (seen here) and finished the basic configuration. The /auth route works correctly if params are provided and creates a user. sign_in and sign_out both successfully create sessions and return some header information. The important parts returned are client, access-token, and uid, which I need to use in future calls to the API. I believe these are invalidated and replaced with each subsequent call. At this part of the flow is where I'm not sure how to proceed. I don't understand how the data in these headers is associated with the user who signed in and how I can validate that they own a resource they request. To summarize the question another way:
How can I sign a user into the API and then validate which user is making subsequent API calls?
My Android app is a task manager, so I need to be able to validate for example that if user 1 requests task 3, that they own that resource. I'm also unsure how to direct index calls using the API. That is, when the index endpoint is hit (/tasks), how can I identify from the data in the API headers which user's tasks I should retrieve.
I haven't been able to find any solid tutorials or samples using devise_token_auth so I'm having trouble stitching together the interaction between the pieces I have now. I know this is a meaty question - thanks in advance for any guidance.
How can I [...] validate which user is making subsequent API calls?
With the current_user method. This is a built-in feature to the devise_token_auth gem.
I need to be able to validate for example that if user 1 requests task 3, that they own that resource
There are many different approaches you could take. You could just write some custom logic in each controller action, using the current_user method (and return 403 forbidden if necessary).
Or, you could use a popular "framework" solution for this such as CanCanCan or Pundit. I, and probably most of the modern community, would recommend Pundit.
I highly advise you to read that library's README, as it's extremely helpful. But for the example above, you could write something like this:
class TasksController
def show
task = Task.find(params[:id])
authorize(task) # !!!
render task
end
end
# app/policies/task_policy.rb
class TaskPolicy
def show?
record.user == user
end
end
(Note that by default, the "user" in Pundit policies calls the method: current_user. This is all explained in the project's README.)
when the index endpoint is hit (/tasks), how can I identify from the data in the API headers which user's tasks I should retrieve
Again, this is all handled as part of Pundit's standard features. You just need to define a TaskPolicy::Scope and call policy_scope(Task) in the controller - as explained here.
I am currently working on an avatar app powered by Rails where users can upload avatars for their user profile.
I would like to use a custom HTTP header to block public upload requests and only allow requests from my apps. How would I go about doing this with Ruby on Rails?
I am uploading the avatars using AJAX so this may be a bit harder. Also I would prefer not to show the header in the public HTML code otherwise it defeats the object of adding it!
If you add
protect_from_forgery
to your application controller, it will block all NON Get requests from 3rd party links. It will add a hidden input value to each form with an authentication token that will be used to check all data that is sent to the servers.
Further reading
http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf
http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html
Rails 3.1 - CSRF ignored?
You could implement a custom HTTP header (say X-Foobar-Validity-Status: valid) and check it in a before_filter.
class YourController < ApplicationController
before_filter :check_header
def check_header
unless request.headers['X-Foobar-Validity-Status'] == "valid"
render json: {"error" => "You are an evil attacker. Go away"}
end
end
end
However, I would consider this a bad idea.
Attackers can read the packet dump of your HTTP requests and add the headers, even with jQuery. See the jQuery.ajax headers option.
Instead of using a proprietary header, I would use User-Agent for this purpose.
Instead, I would sugest using the protect_from_forgery mechanism of rails. It makes your life easier and is more secure. Just fetch the authenticy token by a http request in your app and then send it back with your request. This should keep intruders out.
i want to uses youtube's api within rails.
I need a client which is able to access youtubes api application wide.
therefore i wrote the following application controller
require 'gdata'
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :auth
def auth
#client = GData::Client::YouTube.new
#client.clientlogin('usermail', 'password')
#client
end
end
i am able to use the client now in my controllers which extend ApplicationController.
thats working fine.
but its pretty slow.
is there a way to do the authentication once and using it application wide instead of suing the before_filter which is getting called every single time before i call a method?
best,
philip
This is a web page. Webpages are state-less. Thus you cannot preserve any state. Thus you cannot preserve your login across requests. Thus you have to auth every request.
An alternative would be to only run the before filter on certain controller actions. Right now it runs on every action, which my be not necessary.
Try:
before_filter :auth, :only=> my_action_name
(P.S. That might be the wrong syntax -- I'm confused 'cause rails changes so much -- just look it up)
I am building an API for my rails app i am trying to reuse same controllers for both API and app logic. The requests without authenticating a user is fine but how to implement if some actions need an authentication. I am using authlogic, to edit people need some specific permissions which i am checking in the before_filter. If i implement an API using http basic authentication how do i differentiate ?
I think there are two parts to the answer. Firstly you need to set up http basic auth with authlogic. The details for doing that are contained in this SO answer: Rails: Basic Authentication with Authlogic
Then you need to differentiate between API calls and normal browser calls in your controller. Assuming your API is XML or JSON, you can do that with something like this:
class ThingsController < ApplicationController
before_filter :authorize
def authorize
if params[:format] == 'json' || params[:format] == 'xml'
require_http_auth_user # http basic auth for API access
else
require_user # normal authlogic authentication
end
end
end
It may be worthwhile separating into two separate controllers and including the common functionality via a mixin. That way you can auth the controllers separately.
I'm asking this because I feel that it's making my life overly complicated when I start communicating with rails using ajax or flash.
I know it's nice to protect against CSRF, but couldn't I just check the referer or something instead?
Many users deactivate their referer, mostly not by choice.
But because they're behind a firewall that's blocking it.
Using protect from forgery is the only solution to protect you against CSRF.
But you can deactivate for any action you wish.
In your controller, you add :
skip_before_filter :verify_authenticity_token, :only => :create
This will deactive the token verification for the create action in the controller where you've added the filter.