getting csrf tokens for json post requests to a rails app - json

I have been playing around with using rest-client to access a rails app I have written. I've written a quick script to log in and make a post request. Everything is working but I did have to work round the fact that no authenticity_token is served if you make a request for a form in json. I had to make a regular html request in other get the authenticity_token and then included this in the json I submitted as part of my post request. Basically I have a quick an dirty script like the one below
private_resource = RestClient::Resource.new( 'https://mysite.com')
params = {:user => {:email => 'user#mysite.com', :password => 'please'}}
#log in
login_response = private_resource['users/sign_in'].post(params, :content_type => :json, :accept => :json)
#get cookie
cookie = login_response.cookies
#get json
json_response = private_resource['products/new'].get(:content_type => :json, :accept => :json, :cookies => cookie)
#another request that returns html form with authenticity token
response_with_token = private_resource['products/new'].get( :cookies => cookie)
#extract token
token = Nokogiri::XML(response_with_token).css('input[name=authenticity_token]').first.attr('value')
#update cookie
cookie = response_with_token.cookies
#populate form and insert token
form = JSON.parse(json_response)
form['name'] = "my product"
form['authenticity_token'] = token
#submit the request
private_resource['products'].post(form.to_json, {:cookies => cookie, :content_type => :json, :accept => :json})
There is the option to turn off CSRF protection for json requests but I would rather not do that. I could go the mechanize route or something similar and then I wouldn't worry about json requests with CSRF but I just wanted to play around with doing this stuff with rest-client
I guess I'm just curious to know if there is a reason why no authenticity_token is served for json requests and I'm also wondering if there is a better way of solving the token problem than the pretty hacky approach I've taken here

Put the below code into your application controller :
def verified_request?
if request.content_type == "application/json"
true
else
super()
end
end
And call this method using before_filter .
For more details check :
http://blog.technopathllc.com/2011/09/rails-31-csrf-token-authenticity-for.html
And check this issue in rails : https://github.com/rails/rails/issues/3041

In your app/views/products/new.json.jbuilder, add this:
json.authenticity_token form_authenticity_token
This will insert a key "authenticity_token" with value being the token, so in your json_response you get the token as well. Idea from this answer.

Related

return errors while using API routes in Laravel 8

I'm building a small application to store contacts in the database, I've finished the GET/POST routes, and worked fine, now I'm on the API routes (in order to use AJAX calls). I can store the information if all fields are present in the POST request, nonetheless, If I want to send messages back to the call (to send feedback about why the contact hasn't been stored) the response is sending me to the main route www.myapp.com (with no messages) and I want to send a json back with the "reason".
At this moment I only validate if the 'nombre', 'correo', 'telefono' have information with standard Laravel's request validate method.
This is my LeadController
public function storeApi(Request $request)
{
$request -> validate([
'nombre' => 'required',
'correo' => 'required' ,
'telefono' => 'required'
]);
if(Lead::create($request->all())){
$result[] = ['saved' => true];
}else{
$result[] = ['saved' => false,
'reason' => 'Some data is missing'];
return response()-> json($result);
};
return response()-> json($result);
}
When the record is stored, it does send back the Json {'saved' : true} but when fails It just sends you back to the '/' Route: www.myapp.com
How can I send the messages back to the POST call?
It is redirecting back to "/" because $request->validate() method throws \Illuminate\Validation\ValidationException exception..
There are try ways to handle this request.
Put try catch block around your validate code
Or Handle this expection in app\Exception\Handler.php, and return the response in JSON format.
After some further reading I just change the way the information is validated using the Validator Class:
public function storeApi(Request $request)
{
$validator = \Validator::make($request->all(), ['nombre' => 'required', 'correo' => 'required', 'telefono' => 'required']);
if($validator->fails()){
return response()->json($validator->errors(), 422);
}else {
//ready to store
}
}
This way I don't let the ValidationException exception occurs before sending the feedback to the call.

Can't post JSON data to GitHub API (and probably others)

I'm using HTTP::UserAgent to try and use GitHub API from a program. Here's the program
use HTTP::UserAgent;
my $greeting = (%*ENV<BODY> ~~ /[Mm]erry/)??%*ENV<GREETING>!!%*ENV<HEY>;
my $url = "https://api.github.com/repos/JJ/raku-advent-calendar-article-2019/issues/%*ENV<ISSUE>/comments";
my %headers = Authorization => "token %*ENV<TOKEN>" ;
my %payload = body => $greeting;
my $agent = HTTP::UserAgent.new( useragent => "JJ's Xmas commenter" );
say $agent.post( $url, %payload,
Authorization => "token %*ENV<TOKEN>",
Content-Type => "application/json" );
If the content-type is not established, there's a malformed JSON error. If it's used, however, the error is different: 422 Unprocessable Entity. When using curl or similar, you can usually post directly the JSON string, but post in this case does not admit single strings, or if it's a form, I have no idea what to use as key. Can you please help?

HTTPS: 400 Bad request! Invalid JSON

I am trying to pass a POST method request for a specific URI using the Restclient gem. I am however, continously getting 400 Bad request from the server. I have tried numerous ways of posting the data, with modifications. PFB the current one
require 'minitest'
require 'rest-client'
require 'json'
require 'pry'
require 'uri/https'
#class APITest < Minitest::Test
def setup
response = RestClient.post("", {'userType' => 'nonsso', 'firstName' => 'Justin9', 'isDependentMajor' => true, 'email' => 'randomemail0053#gmail.com', 'dependentName'=> 'Cobb', 'dependentLastName' => 'Cobb', 'lastName' => 'Justin'
}, { "Content-Type" => 'application/json'})
puts response
end
setup
I am at a loss to understand what am I missing here. I tried using the same code, for an other api, with get method, only with headers and it works.
Please can someone let me know, any bad syntax in json I am using for the POST method.
response = RestClient.post("", {'userType' => 'nonsso', 'firstName' => 'Justin9', 'isDependentMajor' => true, 'email' => 'randomemail0053#gmail.com', 'dependentName'=> 'Cobb', 'dependentLastName' => 'Cobb', 'lastName' => 'Justin'
}.to_json, { "Content-Type" => 'application/json'})
Note the to_json.
RestClient serializes the payload in application/x-www-form-urlencoded by default. You have to manually serialize your post data.

Sending JSON Webhook Response

I'm trying to receive a request from a webservice via JSON and send a successful response message back if the token is correct along with some other identifying information, otherwise send a proper error message
post "/hook/foo/bar" do
puts request.env
if request.env['TOKEN'] === "secret_code"
HTTParty.post("https://hook.com/hooks/catch/foo/bar/",
{
:body => #info.to_json,
:headers => { 'Content-Type' => 'application/json', 'Accept' => 'application/json'}
})
[200, {}, "Success"]
else
[400, {}, "Authorization Failed"]
end
The service sending the hook (Zapier) says it sends successfully, but i'm not responding any meaningful data to them that I can use. I believe my formatting for my responses is wrong, but i'm not sure how.
David from the Zapier Platform team here.
Per the sinatra docs, you can return:
An Array with three elements: [status (Fixnum), headers (Hash), response body (responds to #each)]
So you're sending back no hints on the content type and a string as the body. This is valid, and your hook succeeds, but you can do better!
Zapier parses the response from an outgoing hook as JSON, so it's best to send that back.
I've just tested the following example:
require 'sinatra'
require 'json'
get '/' do
'hello world!'
end
post '/hook' do
{message: 'great!'}.to_json
end
and my response was parsed!
If you want to set a status code, honestly the easiest way to do it is with the function status(400) anytime before your return. That being said, a 401 is probably the code you want for Unauthorized", rather than400`. Either way though, zapier will flag that run as an error.
​Let me know if you've got any other questions!

Sinatra Content Type both json and form

I have been looking all over for how to properly check respond to a application/json type as well as a submitted form. I finally got it working on my own with the following code. Can someone explain why it works? Or offer advice on a better solution to achieve the same thing?
post '/login', provides: :json do
p = params
if request.content_type == 'application/json'
params = JSON.parse(request.body.read, :symbolize_names => true)
else
params = p
end
requires(params, :email, :password)
if #user = User.find_by_email(params[:email])
if #user.authenticate(params[:password])
log_user_in(#user)
rabl :login, object: #user
else
error 404, {error: "incorrect credentials"}.to_json
end
else
error 404, {error: "user not found"}.to_json
end
end
JSON requests are submitted in the body of the html request so this works but overriding the params hash is not advisable if using RESTful routes.
Hey Curtis.
Just use ::Rack::JSONBodyParser from rack-contrib:
A Rack middleware that makes JSON-encoded request bodies available in
the request.params hash. By default it parses POST, PATCH, and PUT
requests whose media type is application/json. You can
configure it to match any verb or media type via the :verbs
and :media options.
Examples:
Parse POST and GET requests only
use Rack::JSONBodyParser, verbs: ['POST', 'GET']
Parse POST|PATCH|PUT requests whose Content-Type matches 'json'
use Rack::JSONBodyParser, media: /json/
Parse POST requests whose Content-Type is 'application/json' or 'application/vnd+json'
use Rack::JSONBodyParser, verbs: ['POST'], media: ['application/json', 'application/vnd.api+json']