How do I write a sinatra API to receive a POST from AngularJS $http - ruby

$http({
method: "POST",
url: "http://myapp.herokuapp.com/angular",
data: {message: $scope.newChat}
})
I thought that if I enable it to receive JSON, it would be fine, but I still get a 404 error with an OPTIONS method:
require 'rubygems'
require 'sinatra'
require 'sinatra/json'
require 'json'
post '/angular' do
data = JSON.parse(request.body.read)
json data
end
But it's not working. What do I need to change on either/both the angularJS side and my sinatra server side in order to accept posts and send a response back to the client?

I'm working on the assumption that this is a cross origin request. If this is the case CORS is disabled by default due to security reasons; as it should be.
You will need to enable CORS; in Sinatra I typically use the sinatra-cross_origin extension for this.
sinatra-cross_origin
To enable CORS for the entire service you would use something like follows.
require 'sinatra'
require 'sinatra/cross_origin'
configure do
enable :cross_origin
end
To enable for only specific routes it would look more like follows.
get '/cross_origin' do
cross_origin
"This is available to cross-origin javascripts"
end
Make sure you realize that this opens a big security risk and you are mitigating that risk appropriately.
The following for example will allow any origin to connect, which in the case of an open web service may be what you need.
set :allow_origin, :any
On the other hand you could limit it if possible
set :allow_origin, 'http://example.com'
If you need to open it up as a full web service to any origin then you will want to consider additional security measures such as TLS and OAuth, but that is a bit beyond the scope of this question.

Related

How to mock OAuth2 authentication flow in RSpec?

I am quite new to using RSpec but would like to start testing the (thus far, quite basic) API that I have created, but I am absolutely stumped as to how I can go about mocking the authorization flow of OAuth2, using the oauth and quickbooks-ruby gems. I have researched into this quite extensively today, and am coming up empty handed on any results. Any resources or suggestions would be hugely appreciated.
I am unsure how to even begin approaching actually writing tests, so I don't have any RSpec stuff written yet, but below is shown the two endpoints that I would like to somehow mock, to give an idea of my aim.
get '/callback' do
redirect_uri = "http://localhost:4567/callback"
resp = qbo_client.auth_code.get_token(params[:code], redirect_uri: redirect_uri)
redirect to("/")
end
get '/auth' do
redirect_uri = 'http://localhost:4567/callback'
code_url = qbo_client.auth_code.authorize_url(redirect_uri: redirect_uri, response_type: "code", state: SecureRandom.hex(12), scope: "com.intuit.quickbooks.accounting")
redirect code_url
end
Basically, for a user to authenticate, they hit the /auth endpoint which redirects to Quickbooks for OAuth authorization, this sends the authorization code to the callback endpoint which is then traded for an access token.
I am unsure how to simulate obtaining and exchanging the authorization code, primarily. My best guess at this point is to use a fixture representing the auth-code, mock the exchange with webmock or some comparable tool, but I am very unsure if this is the right approach.
To test the OAuth2 flow in your API, you will need to simulate the responses from the authorization server and ensure that your code handles them correctly. One way to do this is by using a library like VCR or WebMock to record and replay HTTP interactions between your API and the authorization server.
Here is an example of how you could use VCR to record and replay requests to the Quickbooks API:
Install VCR gem() and configure VCR in your spec_helper.rb file:
require 'vcr'
VCR.configure do |config|
config.cassette_library_dir = 'spec/vcr_cassettes'
config.hook_into :webmock
end
RSpec.configure do |config|
config.around(:each) do |example|
VCR.use_cassette("#{example.full_description.parameterize.underscore}") do
example.run
end
end
end
Use the VCR.use_cassette block to wrap your API requests in your test:
describe 'GET /auth' do
it 'redirects to Quickbooks for OAuth authorization' do
VCR.use_cassette('quickbooks_oauth_authorize') do
get '/auth'
expect(last_response).to be_redirect
end
end
end
describe 'GET /callback' do
it 'trades the authorization code for an access token' do
VCR.use_cassette('quickbooks_oauth_token') do
get '/callback', code: '123456'
expect(last_response).to be_redirect
end
end
end
When you run your tests, VCR will record the HTTP interactions and save them to a YAML file in the spec/vcr_cassettes directory. On subsequent test runs, VCR will replay the interactions from the cassette file instead of making real HTTP requests to the authorization server. This allows you to test the OAuth2 flow in a predictable and repeatable way.
In the example above, I used quickbooks_oauth_authorize and quickbooks_oauth_token as the names of the cassette files, but you can use any name that makes sense for your test. You can also pass additional options to the VCR.use_cassette block to customize how VCR records and replays the interactions, such as record: :new_episodes to append new interactions to an existing cassette file.
I hope this helps you get started with testing the OAuth2 flow in your API using RSpec and VCR. Good luck!

Handling CORS for subdomain ajax requests in rack

After looking at other questions and answers I find they don't really cover want I want to do, hence why I'm asking a new question.
So, I built a subdomain matching middleware for rack with the idea that I would host my api in it's own subdomain (api.localhost:3000).
This for the most part works however if I try to send an ajax request the web browser throws a hissy fit about CORS.
So how do I add in CORS for subdomains for my Subdomain matching middleware bearing in mind I'm working in rack for this.
If you can prevent the use of CORS or jsonp I suggest you do. The best solution would be to define a subdirectory to handle your API/restful requests. I had a similar situation and when you force yourself into CORS aka Header Always Set Access-Control-Allow-Origin * then you set yourself up for limitations.
Avoid these limitations when possible. Just my experience. Good luck!
I would do something like:
http://www.domain.com/rest/api/items/1
Where rest is where your web services are handled. Get away from subdomains if you don't need them.
If you must absolutely use CORS and you know that requests are handled by you and only you then you can get away with using jsonp.
jsonp
jsonp essentially wraps your request in a javascript object using the callback function. There are limitations to the amount of data you can handle using this approach, but it is a quick way to get your cross domain requests to work without setting up CORS.
Here is an example of a jsonp request:
$.ajax({
type: "GET",
url: "http://www.domain.com/rest/WEB055S?callback=?",
data: args,
contentType: "application/json; charset=utf-8",
dataType: "jsonp"}).
done(function(data){
alert(data.ERROR);
})...

URL not allowed by Access-Control-Allow-Origin

I am trying to implement OAUTH for accessing Flickr APIs. My AJAX call to flickr.com keeps failing.
Sample Error Message:
XMLHttpRequest cannot load http://www.flickr.com/services/oauth /request_token?oauth_callback=oob&oauth…signature_method=HMAC-SHA1&oauth_timestamp=1368375405647&oauth_version=1.0. Origin http://localhost:8080 is not allowed by Access-Control-Allow-Origin.
Initially I used chrome and read the html file as file://path. I used to get the error 'null not allowed by access-control-allow-origin'. I solved this problem by copying the html file to 'local IIS server', 'local python webserver' and then a 'remote webserver'. I created python web server using > python -m http.server 8080'
I realize my cross browser call to flickr.com using XMLHttpRequest is failing. I tried by various solutions suggested in this forum:
Using newer Chrome 26.0.1410.64 m, which I guess supports CORS
I launched chrome with --disable-web-security
I created a web server using python -m http.server 8080 on local machine and then on a remote machine and copied the html file to the site
I copied file to a local MSFT IIS server
I defined URL in etc/hosts file to avoid numeric IP
I still get the same error (with relevant URL in the error message)
code clipping:
urlString="http://www.flickr.com/services/oauth/request_token?"+
"oauth_callback="+"oob"+'&'+
"oauth_consumer_key="+consumerKey+'&'+
"oauth_nonce="+nonce+'&'+
"oauth_signature="+esignature+'&'+
"oauth_signature_method="+macAlgorithm+'&'+
"oauth_timestamp="+timeStamp+'&'+
"oauth_version=1.0";
$.ajax({
url: urlString,
success:function(data){
alert(data);
}
});
In order to CORS work, both ends must enable it.
The first end is the browser, and, as you are using Chrome 26.*, yours is ok.
The second end is the server:
Before making a GET request to a domain different than the one the page is on, the browser sends an OPTIONS request to that domain. In response to this request, the server should include some headers that tell if a cross-domain request (GET, POST or other) is allowed.
One of those headers is Access-Control-Allow-Origin.
So when you run your page from your file system (file:// "protocol"), the OPTIONS means something like "Flickr, can I make a cross-domain call to you? I'm calling from null". Flickr does not recognize that domain as allowed and returns the error you are getting.
Same way, when you run your page from your local server, the OPTIONS says "(...) I'm calling from localhost:8080". Flickr does not recognize that domain as allowed as well.
The solution:
I don't know the Flickr oauth service, but I know that, as any other service, to make a CORS call to it, the page must be in a domain allowed by it. From your tests, I'm guessing Flickr does't allow many other domains.
But... an alternative to CORS is JSONP. I did a little research, Flickr oauth seems to support it.
Check this page for details: http://www.flickr.com/services/api/explore/flickr.auth.oauth.getAccessToken
There's another question talking about that specific subject:
Is JSONP supported in the new Flickr OAuth API?
About JSONP, this can get you started: How to make a JSONP request from Javascript without JQuery?
It is not possible to implement Oauth 1.0 through just javascript without any server side script. Since the flickr's new authentication process is based on Oauth 1.0a. You got to use a server-side script.
I tried to send the token request using JSONP in FireFox with CORS on(using a third-party add-on) and it worked fine. But without using any add-ons, it's not possible as the response from flickr is in text format(not in a JSON format) and the request fails.
You can either use server-side code for token request. OR Use the deprecated flickr API for authentication.

Ruby http redirection to different subdomain with authentication

I am attempting to scrape the source data of a particular URL using ruby. To begin, I am using Net::http.new to create the http object and then using http.post to pass along the appropriate login data. This works as intended and responds with the appropriate session cookies.
After logging in, and adding the session cookie data to the headers, I then try to access the particular page that I want to scrape. The server responds with a 302 request to an aspx URL on a different subdomain, accompanied with a query string ie. sub.domain.com/path/blah.aspx?md5=jdj456bnn. When I try to load that subdomain using the same technique as I used before, I am met with a user not authorized 302. does anyone know the proper way to load that relocation, or what I could be missing here?
It's very possible a session cookie is being set during the redirect, but your code isn't maintaining it.
"net-http-cheat-sheet" might show how to deal with it, or, look into using Mechanize, which will manage them for you using a cookie jar.

Can't post a form to a different subdomain via Ajax (WARNING: Can't verify CSRF token authenticity)

I trying to send a remote form to a different subdomain (example.domain.com) from the one of the form (domain.com), but i keep getting the warn WARNING: Can't verify CSRF token authenticity in the log, and inspector in chrome tells me that the status of the request is (canceled), with the type application/x-www-form-urlencoded.
This is the form:
button_to "Follow", follow_users_url(subdomain: post.user_username_slug), remote: true
When i remove the remote: true, i got the result i was hopping for. Also, when a try to use the same form in the same subdomain of the action (example.domain.com), i got the correct result.
I found a way to share the cookies in all subdomains (domain: :all in session_store.rb), but i can't found a way to share the token in Ajax requests.
I'm using Rails 3.1.3, Ruby 1.9.3 and jQuery 1.7.1.
Anyone can help me, please?
Edit:
The problem seems to related to CORS. Now i'm trying to find a minimum friction solution to make this (Asynchronous) cross subdomains requests work.
In your POST paramaters, include the field "authenticity_token" with the value returned by the helper *form_authenticity_token*. (It has nothing to do with cookies).
Edit I think you're hitting up against the Same-Origin Policy, which prevents javascript from domain A from communicating with domain B (also applies to subdomains). There is an "override" for this called CORS, which the domain you are talking to must implement.
So assuming you have control over both domain A and B, you can work around this limitation. This explains why "normal" requests work while ":remote => true" requests don't. (The CSRF token error is probably inaccurate.) Here's an article about setting up CORS in Rails (domain B, in my example).
You can set the authenticity token in both controllers to be the same
I think it is
protect_from_forger :secret => 'long_secret_string'
If both controller use the same token you should be able to post across sub domains or other sites. You do open up some cross site scripting holes though

Resources