Communicating with Rails application via POST after OmniAuth authentication - ruby-on-rails

Hi I'm creating a project in which I use OmniAuth to authenticate a user. Its working correctly, except in my application I would like to send information to the server via javascript using POST.
However when I debug the application, I notice it cannot find the user if I send a request using POST - (I believe also the request.env["omniauth.auth"] variable does not exist according when I browsed at the breakpoint).
I'm outputting some debug information back to the application via JSON.
When I change the XMLHttpRequest to use GET, it works and I get back the correct information.
What is the correct usage, perhaps I have an incorrect route? I'm also not sure about using '_method' - would that help? Maybe I'm just looking in the wrong direction period?
How can I send via POST in javascript after authentication with OmniAuth and still retrieve the current_user

So the issue lies in the way protect_from_forgery works. What you'll have to do is validate that your request is safe and the forgery protection should be ignored, you can do this by implementing the forgery_whitelist? at your ApplicationController class:
def forgery_whitelist?
request.user_agent ~= /iPhone/ || self.current_user || super
end
At this method you can implement the logic to whitelist a request and make Rails ignore the forgery protection.

Related

Use Devise Session to Authenticate Doorkeeper API for Rails/Ember app

I'm developing a Rails app along with a corresponding API and contemplating introducing Ember for some particularly dynamic front end components. I'm using Devise for authentication and Doorkeeper to secure API endpoints and manage OAuth tokens.
I don't want to replace the login piece with Ember so the Ember app will likely be initialized once the user logs in on the primary "logged in index" page. I'd like the Ember app to use the public API rather than rendering JSON from my rails-centric controllers, partly for simplicity and partly to force me to keep the API up to date.
Since the user is already logged in, I don't think it makes sense to do the OAuth dance and get a token. Instead I'd like the API to allow requests from clients that have been logged in by Devise (presence of session / cookie). Effectively, you should be able to visit /api/v1/resources.json in a browser once logged in to the app and receive a JSON response. Currently its a 401 Unauthorized.
Does this seem like a reasonable approach? If so, does anyone have experience doing this?
For anyone interested in this in the future, the answer was pretty straightforward:
module Api
module V0
class ApiController < ActionController::Base
before_action :doorkeeper_authorize!, unless: :user_signed_in?
end
end
end
The key part being unless: :user_signed_in?, which is provided by Devise

Using devise_token_auth with jsonapi-resources

I am trying to use devise_token_auth with jsonapi-resources. I have it set up where I can create users and sign in, but I cannot figure out how to access a controller that has needs to authenticate a user first. Here is my controller that I am trying to require authentication:
class FriendsController < JSONAPI::ResourceController
include DeviseTokenAuth::Concerns::SetUserByToken
before_action :authenticate_user!
end
When I try localhost:3000/friends, I get a 401 "Authorized users only." error, so I think it works. I think my main problem is Im not sure what to do with the access-token I get when I sign in. I have tried setting it in the header in my request, but still get the same "Authorized users only" error.
Better a late answer than none... devise_token_auth and jsonapi_resources work pretty well for me in a test setup. But, you need to set the request header correctly. It is not enough to transmit the "auth-token" header, you also heave to transmit the "client" token, the "uid" and the "token-type" header as well. Although I'm not entirely sure if the latter is actually required.
Devise token authentication is quite independent of jsonapi-resources. If you can get token authentication working (as explained in this answer, for example: Custom devise api token auth) you can simply extend your controller (or your ApplicationController) with the authentication concern and it should behave as a normal Rails controller would.

Setting HTTP Headers for Rails form_for

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.

Rails API design without disabling CSRF protection

Back in February 2011, Rails was changed to require the CSRF token for all non-GET requests, even those for an API endpoint. I understand the explanation for why this is an important change for browser requests, but that blog post does not offer any advice for how an API should handle the change.
I am not interested in disabling CSRF protection for certain actions.
How are APIs supposed to deal with this change? Is the expectation that an API client makes a GET request to the API to get a CSRF token, then includes that token in every request during that session?
It appears that the token does not change from one POST to another. Is it safe to assume that the token will not change for the duration of the session?
I don't relish the extra error handling when the session expires, but I suppose it is better than having to GET a token before every POST/PUT/DELETE request.
Old question but security is important enough that I feel it deserves a complete answer. As discussed in this question there are still some risk of CSRF even with APIs. Yes browsers are supposed to guard against this by default, but as you don't have complete control of the browser and plugins the user has installed, it's should still be considered a best practice to protect against CSRF in your API.
The way I've seen it done sometimes is to parse the CSRF meta tag from the HTML page itself. I don't really like this though as it doesn't fit well with the way a lot of single page + API apps work today and I feel the CSRF token should be sent in every request regardless of whether it's HTML, JSON or XML.
So I'd suggest instead passing a CSRF token as a cookie or header value via an after filter for all requests. The API can simply re-submit that back as a header value of X-CSRF-Token which Rails already checks.
This is how I did it with AngularJS:
# In my ApplicationController
after_filter :set_csrf_cookie
def set_csrf_cookie
if protect_against_forgery?
cookies['XSRF-TOKEN'] = form_authenticity_token
end
end
AngularJS automatically looks for a cookie named XSRF-TOKEN but feel free to name it anything you want for your purposes. Then when you submit a POST/PUT/DELETE you should to set the header property X-CSRF-Token which Rails automatically looks for.
Unfortunately, AngualrJS already sends back the XSRF-TOKEN cookie in a header value of X-XSRF-TOKEN. It's easy to override Rails' default behaviour to accomodate this in ApplicationController like this:
protected
def verified_request?
super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
end
For Rails 4.2 there is a built in helper now for validating CSRF that should be used.
protected
def verified_request?
super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
end
I hope that's helpful.
EDIT: In a discussion on this for a Rails pull-request I submitted it came out that passing the CSRF token through the API for login is a particularly bad practice (e.g., someone could create third-party login for your site that uses user credentials instead of tokens). So cavet emptor. It's up to you to decide how concerned you are about that for your application. In this case you could still use the above approach but only send back the CSRF cookie to a browser that already has an authenticated session and not for every request. This will prevent submitting a valid login without using the CSRF meta tag.
Rails works with the 'secure by default' convention. Cross-Site or Cross-Session Request Forgery requires a user to have a browser and another trusted website. This is not relevant for APIs, since they don't run in the browser and don't maintain any session. Therefore, you should disable CSRF for APIs.
Of course, you should protect your API by requiring HTTP Authentication or a custom implemented API token or OAuth solution.

Ruby ways to authenticate using headers?

I am designing an API system in Ruby-on-Rails, and I want to be able to log queries and authenticate users.
However, I do not have a traditional login system, I want to use an APIkey and a signature that users can submit in the HTTP headers in the request. (Similar to how Amazon's services work)
Instead of requesting /users/12345/photos/create I want to be able to request /photos/create and submit a header that says X-APIKey: 12345 and then validate the request with a signature.
Are there any gems that can be adapted to do that? Or better yet, any gems that do this without adaptation?
Or do you feel that it would be wiser to just have them send the API key in each request using the POST/GET vars?
You probably use an authentication library already. It probably has a way to override the way it checks for authentication. Most likely, the method is named authenticated?. Refer to the documentation for the library you are using, though.
I would not have looked for an existing gem, but implemented it myself; doing so shouldn't be too hard. Here's an example boilerplate implementation:
class ApplicationController < ActionController::Base
def authenticated?
current_user.is_a?(User)
end
def current_user
user_from_session || user_from_api_key
end
def user_from_session
# ... use session[:user] or whatever.
end
def user_from_api_key
User.find_by_api_key(request.headers["X-APIKey"])
end
end
That's as specific as I can get, since you don't say anything about your current authentication setup.

Resources