Rails 4 - Endpoint for an API Callback - ruby-on-rails

I recently upgraded from Rails 3.2 to Rails 4 and am now having trouble with one of my app's endpoints.
I'm trying to get an external service (Hellosign) to POST some JSON to my app after a user performs an action. For this to go through, my endpoint has to return a 200 HTTP code and a response body containing the following text: "Hello API Event Received"
This worked fine in Rails 3.2, but I'm having trouble doing this in Rails 4. I'd set up a route for this in config/routes.rb
post 'document' => 'requests#document'
My User model includes has_many :requests, and my Request model includes belongs_to :account. I'm using the latest version of Devise.
A stripped-down version of the Requests Controller is as follows:
class RequestsController < ApplicationController
before_filter :authenticate_user!, except: [:document]
skip_before_action :verify_authenticity_token, :only => [:document]
def document
render text: "Hello API Event Received"
#json = JSON.parse(params["json"])
end
end
I skip Devise authentication and verifying the authenticity token for the document action so that it can actually receive the POST from the API, which has its own authentication protocols.
THE PROBLEM: I'm getting a message from the API that the callback is failing, with an 'Access denied' error. When I check the production logs in Heroku, I get a ERROR invalid body size error message.
My best guess is that something has changed in Rails 4 with Devise and I can no longer use the before_filter and skip_before_action bits to allow the document action to serve as a public-facing endpoint.
Any help would be much appreciated!

Grrr, I found out the problem. The endpoint I specified to the API used http rather than https. Everything works swimmingly now after supplying https to the HelloSign API. However, the endpoint I'd supplied (with http) had worked with Rails 3.2 even after I'd added the SSL in my app...what in the Rails 4 upgrade made this fail? And how to make sense of the `ERROR invalid body size' message? Anyways, thanks to all who supplied suggestions!

Related

How to solve invalid authenticity token error in Rails when sending form data from external page

I have a WordPress page that I built before I learned Rails. On the page there is a form. I want to send the form data to a Rails route. But i get an invalid authenticity token error. How do I satisfy rails with an authenticity token since I understand that they are created by the Rails app itself and therefore would never exist outside of Rails?
Since this sounds like a separate app that you want to use to post data to a rails endpoint, you probably don't care about CSRF issues for the controller action that handles this. You could disable the authenticity token verification for your controller action with:
# inside your controller class
skip_before_action :verify_authenticity_token, only: [:your_wordpress_action]

Rails 6 `protect_from_forgery with: :null_session` not working

In an effort to learn React and using Rails together I am in the process of building a simple "Todo" app. Im using axios to make requests to my rails api within react components.
So far everything was working fine until I made my first POST request. Im pointing to the right place and sending fine params but saw I was getting Can't verify CSRF token authenticity.. Upon further googling it appears that adding:
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
end
Would fix my issue. But despite server restarts/etc... I still receive the same error when sending a POST request. Also adding prepend: true does not help either (as was suggested by another member).
Is there something im missing? This seems like a straightforward problem from what i've read but it doesn't seem to be working for me.
Im on Rails 6.0
if you just want the error to go away, use:
class ApplicationController < ActionController::Base
skip_forgery_protection
end
But if you would like to actually take advantage of the Rails forgery protection, then add an "X-CSRF-Token" header to your post request from React. The value for this header can be retrieved by javascript from the page's head element, in a meta tag included by Rails. For example (using jQuery):
var csrf_token_value = $('meta[name="csrf-token"]').attr('content')

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 curl to access backend API of Rails app that implements devise

We have a new web app constructed by another developer. My job was to implement a backend API for other programs to use to exchange XML data with the database. It was going pretty well, until the main app implemented devise to authenticate users. I figured I'd be able to tweak the curl command to log in then submit/request data, but it isn't going very well. I'm trying things like
curl --cookie-jar ~/Desktop/cjar --data "user[login]=<username>" --data "user[password]=<pwd>" --data "commit=Sign in" localhost:3000/users/sign_in
followed by
curl --cookie ~/Desktop/cjar --data "<root_node></root_node>" localhost:3000/my_update_method
my latest error message is
WARNING: Can't verify CSRF token authenticity
Completed 401 Unauthorized in 1ms
I guess that's better than the redirection messages I was getting, but I'm still not close to knowing what's going on.
I've found a few related posts that make me think this is partly related to devise, and partly related to automatic authentication done by Rails. When I look at the info being passed by the web page, I see authenticity_token and utf8 params that I don't know how to construct for my curl command.
I can keep trying stuff, but I'm new to both curl and authentication, so I'm sort of shooting in the dark, and hoping that someone can save me some time. I guess my questions are:
Can this be done?
Should this be done? If not, what other options do I have?
One post suggested a way around the authentication, but is is possible for only the curl commands to require a login/pwd, but bypass any other validation, without affecting the main web app?
UPDATE 1:
Thanks for all the help, I appreciate it. The response to the first curl command (listed above) is now just a redirection to the login page, so logging in doesn't seem to take. Does anyone have any suggestions about where to start debugging that? Nothing is ever getting into the application, so there's no logging there to look at. Here's the content of the cookie, if it means anything to anyone:
# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This file was generated by libcurl! Edit at your own risk.
#HttpOnly_localhost FALSE / FALSE 0 _glow_session BAh7B0kiCmZsYXNoBjoGRUZvOiVBY3Rpb25EaXNwxhc2g6OkZsYXNoSGFzaAk6CkB1c2VkbzoIU2V0BjoKQGhhc2h7ADoMQGNsb3NlZEY6DUBmbGFzaGVzewY6C25vdGljZUkiHFNpZ25lZCBpbiBzdWNjZXNzZnVsbHkuBjsAVDoJQG5vdzBJIg9zZXNzaW9uX2lkBjsARkkiJTIwODc1Y2VjM2ViNzlmZDE3ZjA4ZjVmMDAxNWMxMDU4BjsAVA%3D%3D--d90722b6da386630b33f57902447b440f30d0b2a
I've added skip_before_filter :verify_authenticity_token to the controller that handles the api requests, and that eliminated the error I was seeing before.
UPDATE 2:
After some troubleshooting and debugging, I think I may actually be getting logged in okay, but when I execute the second curl, the development.log file shows
Started POST "/upsert_experiment" for 127.0.0.1 at 2013-10-16 12:14:12 -0500
Processing by WebServiceController#upsert_experiment as */*
Parameters: {"experiment_xml"=>"<experiment></experiment>"}
Completed 401 Unauthorized in 1ms
which leads me to think that the authorization is still the problem.
UPDATE 3:
I think the problem is just that skip_before_filter :verify_authenticity_token isn't working.
I have
class ApplicationController < ActionController::Base
rescue_from DeviseLdapAuthenticatable::LdapException do |exception|
render :text => exception, :status => 500
end
protect_from_forgery
before_filter :authenticate_user!
end
and
class WebServiceController < ApplicationController
skip_before_filter :verify_authenticity_token
...
end
With this setup, I get "Completed 401 Unauthorized in 1ms". If I comment out the protect_from_forgery line in the superclass, it just works, so the forgery protection is definitely the problem. I just don't know how to solve it.
Rails CSRF protection is preventing you from making this request.
From rails documentation
CSRF protection automatically include a security token, calculated from the current session and the server-side secret, in all forms and Ajax requests generated by Rails. You won't need the secret, if you use CookieStorage as session storage. If the security token doesn't match what was expected, the session will be reset. Note: In Rails versions prior to 3.0.4, this raised an ActionController::InvalidAuthenticityToken error.
If you want to skip it, add this line to your controller
skip_before_action :verify_authenticity_token, :only => ["your_update_action"]
CSRF stands for Cross Site Request Forgery and is a security protection you can read more about here. Essentially rails generates a random token included in the form rendered on the page and it expects to get that token back on form submit. You don't need this protection for an api, but it is important for the web app side. See this question for methods to disable the CSRF token.
One approach you might take is to use the token_authenticable Devise module and authenticate your requests with auth_token=#{token}, then skip CSRF protection if an auth token is present.
You're missing a CSRF token. This is to prevent cross site request forgery.

How to make sure Rails API is secured from CSRF?

I've been developing Rails app with REST API for access from mobile application.
It works quite well. When user logs in from mobile application, he gets auth_token that he uses in his future requests to API. The issue is that API is also accessible from web by going to path /api/v1/... and because of this, it has to be protected from CSRF.
I have BaseApiController class which inherits from ApplicationController that has protect_from_forgery "enabled". Here's example:
class Api::V1::BaseApiController < ApplicationController
# ...
end
class ApplicationController < ActionController::Base
protect_from_forgery
# ...
end
Now, when I do non-GET requests to my API, with auth_token, my request gets completed successfully, but in the logs I can see the famous WARNING: Can't verify CSRF token authenticity. If I remove protect_from_forgery from my BaseApiController, I don't get any warnings (obviously), but then my API is vulnerable to CSRF attacks (I made a simple HTML form that successfully changes the data across domains when there's no protect_from_forgery).
My question is: How to assure my API stays secure, but also remove the warning when doing non-GET requests?
Here's one of the solutions I've come up with, but it looks more like a hack and executes one extra DB query:
class Api::V1::BaseApiController < ApplicationController
# ...
def verified_request?
super || User.where(authentication_token: params['auth_token']).count > 0
end
end
More details about the project: Rails 3.2.14, Devise, AngularJS. The project's source code can be found here.
You may see people suggest that CSRF is not an issue for API requests (there is no state to begin with, so what is there to hijack anyhow?), so some suggest the following to simply eliminate the warning:
skip_before_filter :verify_authenticity_token, :only => [:your_method]
However, there was some commentary that it is possible to commit CSRF with text/plain using various Flash and Java-based methods. I believe that was the reason for the security patch in rails a while back: http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails/
In any event, a good solution that actually checks for an authenticity token can be found here: WARNING: Can't verify CSRF token authenticity rails
It involves actually setting the header in your request.
Good luck!

Resources