Invalid authenticity token when POSTing to a Rails API - ruby-on-rails

I have a web application that gets & stores some data through a Rails API.
When I try to "post" to the Rails API, I get an error ActionController::InvalidAuthenticityToken. I know one option is to simply disable the authenticity token requirement on my Rails controller.
Is it possible to have my web app providing a correct authenticity token when it calls the Rails API? How can I do this?

2 Part answer for you here.
First if you are going to be using Rails as an API I would recommend you use another way of validating that the user making the request is actually the user they say they are such as creating a unique token upon account creation or login that can be returned in the initial response and provided as a HTTP header in subsequent requests. If you are worried about the security of this you could optionally base64 encode the key plus some other value and decode it server side before comparison.
If you still wish to use the CSRF method baked in to Rails you can do so as long as the user is making a request from the webapp using AJAX or whatever. If you have the csrf_meta_tags ERB in the header of your layout file you can get the value and set it in the X-CSRF-Token HTTP header. Using jQuery it may look something like:
$.ajaxPrefilter(function(options, originalOptions, xhr) {
var token = $('meta[name="csrf-token"]').attr('content');
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
});
This would cause it to be added to every ajax request made using jQuery.

Related

Setting and retrieving custom HTTP header in Rails Monolithic App

I have rails login page and after user authentication I am trying to generate JWT token(rather than using the session ID) for authorization the next subsequent request.
I have set the custom header in response and redirecting the request in next page.
response.headers["access_token"] = "token"
How can I get those headers in rails controller?
To me this task seems impossible as server is sending some custom header in response but client/browser is not sending/appending custom header in the subsequent request.
Any help would be really appreciated.

How to pass CSRF token for Rails post request in paw-app

I am attempting to use the Paw app to test out a REST API build in Rails 4.2.4. I have a create method which expects JSON as input. The request appears to pass the correct information from Paws as structure, but the Rails API is failing with the following error:
422 Unprocessable Entity
Can't verify CSRF token authenticity
When writing the JavaScript front end, the request must have a header appended to the request, of the form:
webix.ajax().headers({'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')}).post("geo_wells.json", parms), function(text){webix.message("posted");});
That all works fine for getting the CSRF satisfied consistently, although it's the parms I am trying to use Paws to help sort out. But I can't get that meta token in Paws. No idea how to actually add that to the header of the request. Tried creating a cookie and all response cookies, but that didn't do it either.
Any ideas? I'd like to purchase the tool, but if it can't do this, it's not of that much use to me. I would prefer not to up and disable the CSRF as is so often suggested. I'm hoping for a robust answer that would still extend to testing of the fully CSRF enabled API.
This Paw.app article had the answer.
First, install the RegExMatch extension.
See: https://paw.cloud/extensions/RegExMatch
With the Ruby on Rails getting started set up, the regex required to get the csrf token was below:

Can someone explain this paragraph to me from the Rails Security Guide?

See here: http://guides.rubyonrails.org/security.html
[Rails CSRF protection] will 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.
I'm really hoping that someone could explain to my why the server side secret is not needed if I store the entire session in a cookie.
The reason I ask is because I am trying to figure out if there is a way to generate CSRF tokens in a 100% client-side application (ember.js) where I won't have Rails inserting a CSRF token on all forms. I can certainly write to cookies using JavaScript, and I can certainly hash various values.
If there is some way to generate a cookie client-side using JavaScript, without a server-side secret, and being able to verify on the server please let me know!
Did you try googling this?
There is plenty of information on jQuery/ember.js/Rails CSRF out there. jQuery has an $.ajaxPrefilter which you can use to append the CSRF token from your META tags to the request headers (and having said META tags existing on page for a single page app shouldn't be a problem).
This article provides one implemenation that uses this method (the example is in Coffeescript).
$ ->
token = $('meta[name="csrf-token"]').attr('content')
$.ajaxPrefilter (options, originalOptions, xhr) ->
xhr.setRequestHeader('X-CSRF-Token', token)
Here is another implemenation extending the DS.RESTAdapter.
All Rails is doing in a 'normal' application is adding the CSRF token as a hidden input field to your forms. Whether coming this way or as an HTTP request header, it's picked up on by rails just the same.
I think it might be if using CookieSessionStore in Rails, there will be a secret token used to sign cookie. Rails may use that secret token instead to verify

CSRF token problem on requests from outside the browser to a Rails server

I need to make an HTTP POST request from outside the browser, but the Rails back-end is not accepting the authentication (error 401). I know I need to pass a CSRF token in such cases, but it's not working.
When I make the request through a form on a browser, it works as expected, but when I try to simulate an identical request (in terms of headers and cookies) from outside the browser (using curl, for example), the authentication doesn't work.
Two small changes allowed me success without a browser: (1) turning off protect_from_forgery, which validates the CSRF or (2) using GET instead of POST for the request. In both cases, passing the cookie is enough. That means the problem is definitely related to CSRF stuff.
So, my question is: how can I make a CSRF-protected HTTP POST to a Rails server without using a browser?
To clarify, the process is broken in three steps:
Login: returns a cookie to identify the session;
New: a GET request that returns the CSRF token to be used later (uses the cookie);
Create: a POST request that submits the information I want to create (uses both the session cookie and the CSRF token).
The only step which fails is the third one.
Assuming your CSRF token is cookie-based, then the program you use to make your requests needs to track cookies. Check out the --cookie-jar option in curl.

Turning off authenticity token in Rails 2 for web services?

Instead of just filling out the form in HTML it should also be possible to just send a post request containing the params.. Is it possible to turn off the authenticity token if, for example, the Accept flag is set to 'application/JSON' in the HTTP header?
The request forgery protection works on the basis of checking the content-type of requests and it only checks the requests that can be made by a browser. No browser is able to generate a request with the content-type set to "application/json" for example. That's why the rails forgery protection routine won't check it. So, if you want to make a json request to your application, set the content-type header to "application/json" and it should work.
I know there is a way to turn it off for a controller or an action. Not sure about the content type. Wouldn't it be easier to just add the authenticity token to every json request? There are quite few articles around the web how to do it (for example here and here).
Wouldn't it be easier to just add the authenticity token to every json request?
Yes, but then the client would have to send a request first just to get the token and then another with the actual POST request, which does not make sense IMHO..

Resources