Reputation: 21513
I want to make POST request
to my local dev, like this:
HTTParty.post('http://localhost:3000/fetch_heroku',
:body => {:type => 'product'},)
However, from the server console it reports
Started POST "/fetch_heroku" for 127.0.0.1 at 2016-02-03 23:33:39 +0800
ActiveRecord::SchemaMigration Load (0.0ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by AdminController#fetch_heroku as */*
Parameters: {"type"=>"product"}
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 1ms
Here is my controller and routes setup, it's quite simple.
def fetch_heroku
if params[:type] == 'product'
flash[:alert] = 'Fetch Product From Heroku'
Heroku.get_product
end
end
post 'fetch_heroku' => 'admin#fetch_heroku'
I'm not sure what I need to do? To turn off the CSRF would certainly work, but I think it should be my mistake when creating such an API.
Is there any other setup I need to do?
Upvotes: 151
Views: 236435
Reputation: 161
If you are doing an api base website without csrf authentication, just put this in the controller
skip_before_action :verify_authenticity_token
But this expose the api endpoints to everyone
To overcome this issue, in our app(which is api based + front end in react) we are passing this in the headers
.
.
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": document.querySelector("meta[name='csrf-token']").content,
},
.
.
Upvotes: 11
Reputation: 101811
Cross site request forgery (CSRF/XSRF) is when a malicious web page tricks users into performing a request that is not intended for example by using bookmarklets, iframes or just by creating a page which is visually similar enough to fool users.
The Rails CSRF protection is made for "classical" web apps - it simply gives a degree of assurance that the request originated from your own web app. A CSRF token works like a secret that only your server knows - Rails generates a random token and stores it in the session. Your forms send the token via a hidden input and Rails verifies that any non GET request includes a token that matches what is stored in the session.
However in an API thats intended to be used cross site and even serve non-browser clients its not very useful due to the problems with cross-domain cookies and providing CSRF tokens.
In that case you should use a token based strategy of authenticating API requests with an API key and secret since you are verifying that the request comes from an approved API client - not from your own app.
You can deactivate CSRF as pointed out by @dcestari:
class ApiController < ActionController::Base
protect_from_forgery with: :null_session
end
Updated. In Rails 5 you can generate API only applications by using the --api
option:
rails new appname --api
They do not include the CSRF middleware and many other components that are superflouus.
Upvotes: 161
Reputation: 254
The simplest solution for the problem is do standard things in your controller or you can directely put it into ApplicationController
:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, prepend: true
end
Upvotes: 0
Reputation: 2872
If you want to exclude the sample controller's sample action
class TestController < ApplicationController
protect_from_forgery except: :sample
def sample
render json: @hogehoge
end
end
You can to process requests from outside without any problems.
Upvotes: 6
Reputation: 5125
If you're using Devise, please note that
For Rails 5,
protect_from_forgery
is no longer prepended to thebefore_action
chain, so if you have setauthenticate_user
beforeprotect_from_forgery
, your request will result in "Can't verify CSRF token authenticity." To resolve this, either change the order in which you call them, or useprotect_from_forgery prepend: true
.
Upvotes: 14
Reputation: 52198
If you only want to skip CSRF protection for one or more controller actions (instead of the entire controller), try this
skip_before_action :verify_authenticity_token, only [:webhook, :index, :create]
Where [:webhook, :index, :create]
will skip the check for those 3 actions, but you can change to whichever you want to skip
Upvotes: 7
Reputation: 1795
Since Rails 5 you can also create a new class with ::API instead of ::Base:
class ApiController < ActionController::API
end
Upvotes: 27
Reputation: 1848
Another way to turn off CSRF that won't render a null session is to add:
skip_before_action :verify_authenticity_token
in your Rails Controller. This will ensure you still have access to session info.
Again, make sure you only do this in API controllers or in other places where CSRF protection doesn't quite apply.
Upvotes: 128
Reputation: 863
There is relevant info on a configuration of CSRF with respect to API controllers on api.rubyonrails.org:
⋮
It's important to remember that XML or JSON requests are also affected and if you're building an API you should change forgery protection method in
ApplicationController
(by default::exception
):class ApplicationController < ActionController::Base protect_from_forgery unless: -> { request.format.json? } end
We may want to disable CSRF protection for APIs since they are typically designed to be state-less. That is, the request API client will handle the session for you instead of Rails.
⋮
Upvotes: 28