Access an API from local server - javascript

I am trying to call the CTA API (http://www.transitchicago.com/developers/bustracker.aspx) from my local Wamp server. However, when doing the fetch via backbone collection I get:
XMLHttpRequest cannot load http://www.ctabustracker.com/bustime/api/v1/getroutes?key=xx. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
collection:
define([
'models/route',
'core'
], function (Route) {
return Backbone.Collection.extend({
initialize: function () {},
model: Route,
//url: function () {
// return 'http://www.ctabustracker.com/bustime/api/v1/getroutes?key=xx';
//},
url: function () {
return '/apiproxy.php?method=getroutes';
},
});
});
I know this is a common issue but haven't found a concise answer yet.
How can I resolve this issue?
Update
Added the apiproxy but am getting this response:
Remote Address:127.0.0.1:80
Request URL:http://localhost/apiproxy.php?method=getroutes
Request Method:GET
Status Code:200 OK
Request Headersview parsed
GET /apiproxy.php?method=getroutes HTTP/1.1
console:
responseText: "$url = "http://www.ctabustracker.com/bustime/api/v1/{$_GET['method']}? key=xx";
↵echo file_get_contents($url);
SyntaxError {stack: (...), message: "Unexpected token $"}
message: "Unexpected token $"
stack: (...)

You can solve this problem, but it's not a simple case of "add this line to your JavaScript and everything will be fine."
You're running up against the same-origin security policy built into every web browser. 'Origin' basically means 'the same site'; my JavaScript on example.com can access whatever it likes on example.com, but it's not allowed to read anything from demonstration.com, example.net, or api.example.com. That's got a different origin. Here's a table of what counts as the same origin.
Without it, I could write a web page that steals all your gmail and private Facebook photos. My malicious JavaScript would make web requests to gmail.com and facebook.com, find the links to your emails & photos, load that data too, and then send it off to my own server.
Obviously, some web pages are designed to be used by other people. APIs, for instance, generally want to allow access to their data so people can build web apps. The people building those APIs can serve their content with Access-Control- headers that tell browsers it's OK to allow requests from other sites. This is called CORS - Cross-Origin Resource Sharing. The reason you get that error message is because the ctabustracker.com developers haven't added any CORS headers. Thus, you can't access their API from your JavaScript.
So what's the solution? you have two options:
Email the ctabustracker.com admins and ask them to add CORS headers to allow access from other domains. This is the least work for you, but you're at the mercy of the knowledge, infrastructure, & promptness of their development team.
Write your own proxy server.
The same-origin policy is only standing in your way in your JavaScript. You can do whatever you like on the server; at its simplest, you could create an apiproxy.php along these lines:
$allExceptMethod = $_GET; // PHP arrays are copy-by-value
unset($allExceptMethod['method']);
$url = "http://www.ctabustracker.com/bustime/api/v1/{$_GET['method']}?key=xx&" . http_build_query($allExceptMethod);
echo file_get_contents($url);
And then access it from your JavaScript as /apiproxy.php?method=getroutes, and pass extra parameters in via a standard query string (for instance, /apiproxy.php?method=test&foo=bar&cat=dog results in a request to http://www.ctabustracker.com/bustime/api/v1/test?key=xx&foo=bar&cat=dog). Now your JavaScript is making a request to your own server, so you won't have any problems with the same-origin policy.
You can, of course, make your proxy as smart as you like. It could cache responses, convert your XML to JSON, pre-fetch the results for likely next requests, or 100 other things that may be useful for your app.

Related

Making Get request to Yammer API works using Postman tool but not with Vue-Resource

I am trying to integrate Yammer API in my Vue.JS project, for Http calls I am using Vue-Resource plugin. While making GET Http call to get posts from Yammer it gives me following error -
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
I tried postman tool and that gives successful response, but when I try to run the same thing in my Vue.JS project using Vue-Resource plugin it wont work.
The Vue.JS code snippet -
function(){
this.$http.get("https://www.yammer.com/api/v1/messages/my_feed.json").then((data)=>{
console.log(data);
});
In main.vue file i have -
Vue.http.interceptors.push((request, next) => {
request.headers.set('Authorization', 'Bearer my_yammer_token')
request.headers.set('Accept', '*/*')
next()
})
Then I tried the code snippets provided by Postman tool for jquery, that too not working.
jQuery code -
var settings = {
"url": "https://www.yammer.com/api/v1/messages/my_feed.json",
"method": "GET",
"timeout": 0,
"headers": {
"Authorization": "Bearer my_yammer_token",
"Cookie": "yamtrak_id=some_token; _session=some_token"
},
};
$.ajax(settings).done(function (response) {
console.log(response);
});
Though, I found similar questions but nothing worked for me.
I am working this to resolve from last 2 days but getting failed again and again. Please guide/help me.
A browser has higher security requirements than a request in PostMan. In a browser, you are only allowed to make XHR requests to your own current host (combination of domain + port) but not to other remote hosts. To nevertheless make a request to a remote host, you can use the browser built-in CORS. By using this, your browser makes a pre-flight request to the remote host to ask if the current page is allowed to request from that host. This is done via the Access-Control response headers. In your case, this header is probably missing or not allowing your page to access, which is why the request does not go through. Please read further into that topic.
However, in your case, using CORS probably won't be a solution for two reasons: To use CORS, the remote host must present a header which allows every requesting host (*) or your specific one. If you cannot set that setting anywhere on the remote host, it won't work. Second, it is not safe to place your authorization token into client-side JavaScript code. Everybody can just read your JS code and extract the authorization token. For that reason, you usually make the actual API call from the server-side and then pass the data to the client. You can use your own authentication/authorization against your server and then use the static authorization key on the server to request the data from the remote host. In that case, you'll never expose the authorization key to your user. Also, on the server-side, you do not have to deal with CORS as it works just like PostMan or curl as opposed to a browser.

How to handle CORS in a service account call to Google oAuth?

I access a Google calendar using a service account. This means that the owner of the calendar will not be prompted for his authorization.
This works fine in Python, where I make a requests call to https://accounts.google.com/o/oauth2/token with a specific body. This gets me back a token I can use later.
I now need to have an application running in a standalone browser (Chrome - without user interaction) and tried to directly port this call as
fetch('https://accounts.google.com/o/oauth2/token',
{
method: 'POST',
body: JSON.stringify(body),
})
.then(function(res) { return res.json(); })
.then(function(data) { alert(JSON.stringify(data)) })
but I get a reply from Google
Failed to load https://accounts.google.com/o/oauth2/token: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://devd.io' is therefore not allowed access. The
response had HTTP status code 400. If an opaque response serves your
needs, set the request's mode to 'no-cors' to fetch the resource with
CORS disabled.
My limited understanding of CORS (a previous answer was a very good read) is that
No 'Access-Control-Allow-Origin' header is present on the requested
resource
means that it is not present in the response headers, which means that Google does not want me to access its resources via JS when ran from the browser (which points to something else that https://accounts.google.com).
This may be a good idea but I control all elements, from the code to the browser and would like to get that token the same way I get it in a non-browser environment, specifically my working Python code.
How can I tell https://accounts.google.com to send me back a Access-Control-Allow-Origin header which tell my browser that it is OK to accept the call?
You can't.
Client side and server side code need to interact with OAuth in different ways.
Google provide documentation explaining the client side process.
Importantly, part of it involves redirecting to Google's servers instead of accessing them with fetch or XMLHttpRequest.
#Quentin's answer "You can't" is the right one for my question ("how can I force the server to send back the right header").
This is a decision at Google not to provide this header, effectively cutting off any non-interactive applications.
As a solution, I will look at
how to force the browser not to take into account the security mechanisms provided by CORS (there seems to be some ways through extensions or command-line arguments, I will update this answer once I find it)
or write an intermediate layer which will query the data for me and pass them verbatim to the application (this is equivalent, in my case, of just making the query from JS - but it adds an extra layer of code and server)

AJAX request gets "No 'Access-Control-Allow-Origin' header is present on the requested resource" error

I attempt to send a GET request in a jQuery AJAX request.
$.ajax({
type: 'GET',
url: /* <the link as string> */,
dataType: 'text/html',
success: function() { alert("Success"); },
error: function() { alert("Error"); },
});
However, whatever I've tried, I got XMLHttpRequest cannot load <page>. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:7776' is therefore not allowed access.
I tried everything, from adding header : {} definitions to the AJAX request to setting dataType to JSONP, or even text/plain, using simple AJAX instead of jQuery, even downloading a plugin that enables CORS - but nothing could help.
And the same happens if I attempt to reach any other sites.
Any ideas for a proper and simple solution? Is there any at all?
This is by design. You can't make an arbitrary HTTP request to another server using XMLHttpRequest unless that server allows it by putting out an Access-Control-Allow-Origin header for the requesting host.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
You could retrieve it in a script tag (there isn't the same restriction on scripts and images and stylesheets), but unless the content returned is a script, it won't do you much good.
Here's a tutorial on CORS:
http://www.bennadel.com/blog/2327-cross-origin-resource-sharing-cors-ajax-requests-between-jquery-and-node-js.htm
This is all done to protect the end user. Assuming that an image is actually an image, a stylesheet is just a stylesheet and a script is just a script, requesting those resources from another server can't really do any harm.
But in general, cross-origin requests can do really bad things. Say that you, Zoltan, are using coolsharks.com. Say also that you are logged into mybank.com and there is a cookie for mybank.com in your browser. Now, suppose that coolsharks.com sends an AJAX request to mybank.com, asking to transfer all your money into another account. Because you have a mybank.com cookie stored, they successfully complete the request. And all of this happens without your knowledge, because no page reload occurred. This is the danger of allowing general cross-site AJAX requests.
If you want to perform cross-site requests, you have two options:
Get the server you are making the request to to either
a. Admit you by putting out a Access-Control-Allow-Origin header that includes you (or *)
b. Provide you with a JSONP API.
or
Write your own browser that doesn't follow the standards and has no restrictions.
In (1), you must have the cooperation of the server you are making requests to, and in (2), you must have control over the end user's browser. If you can't fulfill (1) or (2), you're pretty much out of luck.
However, there is a third option (pointed out by charlietfl). You can make the request from a server that you do control and then pass the result back to your page. E.g.
<script>
$.ajax({
type: 'GET',
url: '/proxyAjax.php?url=http%3A%2F%2Fstackoverflow.com%2F10m',
dataType: 'text/html',
success: function() { alert("Success"); },
error: function() { alert("Error"); }
});
</script>
And then on your server, at its most simple:
<?php
// proxyAjax.php
// ... validation of params
// and checking of url against whitelist would happen here ...
// assume that $url now contains "http://stackoverflow.com/10m"
echo file_get_contents($url);
Of course, this method may run into other issues:
Does the site you are a proxy for require the correct referrer or a certain IP address?
Do cookies need to be passed through to the target server?
Does your whitelist sufficiently protect you from making arbitrary requests?
Which headers (e.g. modify time, etc) will you be passing back to the browser as your server received them and which ones will you omit or change?
Will your server be implicated as having made a request that was unlawful (since you are acting as a proxy)?
I'm sure there are others. But if none of those issues prevent it, this third method could work quite well.
you can ask the developers of that domain if they would set the appropriate header for you, this restriction is only for javascript, basically you can request the ressource from your server with php or whatever and the javascript requests the data from your domain then
Old question, but I'm not seeing this solution, which worked for me, anywhere. So hoping this can be helpful for someone.
First, remember that it makes no sense to try modifying the headers of the request to get around a cross-origin resource request. If that were all it took, it would be easy for malicious users to then circumvent this security measure.
Cross-origin requests in this context are only possible if the partner site's server allows it through their response headers.
I got this to work in Django without any CORS middleware by setting the following headers on the response:
response["Access-Control-Allow-Origin"] = "requesting_site.com"
response["Access-Control-Allow-Methods"] = "GET"
response["Access-Control-Allow-Headers"] = "requesting_site.com"
Most answers on here seem to mention the first one, but not the second two. I've just confirmed they are all required. You'll want to modify as needed for your framework or request method (GET, POST, OPTION).
p.s. You can try "*" instead of "requesting_site.com" for initial development just to get it working, but it would be a security hole to allow every site access. Once working, you can restrict it for your requesting site only to make sure you don't have any formatting typos.

Why does Ajax give me a cross origin error when I can make the request from PHP?

I can make a GET request from PHP and get the correct response. This is the function I use:
PHP
function httpGet($url)
{
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch,CURLOPT_HEADER, false);
$output=curl_exec($ch);
curl_close($ch);
return $output;
}
A simple example:
$fakevalue='iamfake';
$url="http://fakeurl.com?fakeparameter=".$fakevalue;
$jsondata= httpGet($url);
$fake_array = json_decode($jsondata, true);
$weed_var=$fake_array['weeds']; // successfully obtained weed.
This function returns the response from the server.
Now I am trying the same HTTP GET request in AJAX, but I can't get the response.
Initially I thought the problem was with the JavaScript function that I use. Google provided with me lots of JavaScript functions for performing the HTTP GET request but they all had the same problem. The request returns an error instead of the data that I got when I used PHP.
JAVASCRIPT
var fakevalue = "iamfake";
var fake_data = {
fakeparameter: fakevalue
};
$.ajax({
url: "http://fakeurl.com",
data: fake_data,
type: "GET",
crossDomain: true,
dataType: "json",
success: function(a) {
$("#getcentre").html(a);
},
error: function() {
alert("Failed!");
}
});
Error from JavaScript
XMLHttpRequest cannot load http://fakeurl.com?fakeparameter=fakevalue. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.`
I know you are going to tell me to use CORS, but if it was because of the absence of 'Access-Control-Allow-Origin' header, then how did I get response for the same service in PHP?
With PHP (or anything else running on your server, or a standalone application (including those installed as a browser extension)), you are requesting data from Bob's server using your credentials (your cookies, your IP address, your everything else).
With Ajax, you are asking Alice's browser to request data from Bob's server using her credentials and then to make that data available to your JavaScript (which can then send it back to your server so you can see it yourself).
Bob might give different data to Alice then he would give to you. For example: Bob might be running Alice's eBanking system or company intranet.
Consequently, unless Bob's server tells Alice's browser that it is OK to make that data available to you (with CORS), the browser will prevent your JavaScript from accessing that data.
There are alternatives to CORS, but they involve either distributing the data using a file type that isn't designed to be a data format (JSONP) (which also requires Bob's server to cooperate) or having your server fetch the data from Bob and then make it available through a URL on your server (or some combination of the two like YQL does) (which means that you get the data Bob will give to you and not the data Bob will give to Alice).
Simple answer: PHP isn't affected by CORS. It is a restriction placed by the browser on client-side code, so that the accessed URL gets to allow or deny the request.
You are running into CORS, because you are doing an XMLHttpRequest request from your browser to a different domain than your page is on. The browser is blocking it as it usually allows a request in the same origin for security reasons. You need to do something different when you want to do a cross-domain request. Try this: http://www.html5rocks.com/en/tutorials/cors/
If you only want to launch a GET request, you might try using JSONP datatype="jsonp".
Examples: http://www.sitepoint.com/jsonp-examples/

Cross Origin Resource Sharing (CORS) and Javascript

As an example case let's take this url: http://api.duckduckgo.com/?q=computer&format=json (CORS not enabled on this server!)
We can access the contents from this URL from any popular browser as a normal URL, browser has no issues opening this URL nor the server returns any error.
A server-side language like PHP/RoR can fetch the contents from this URL without adding any additional headers or special server settings. I used following PHP code and it simply worked.
$url='http://api.duckduckgo.com/?q=computer&format=json';
$json = file_get_contents($url);
echo $json;
I just started working in javascript framework, AngularJS. I used following code...
delete $http.defaults.headers.common['X-Requested-With'];
var url="http://api.duckduckgo.com/?q=computer&format=json";
$http.get(url)
.success(function(data) {
$scope.results=data;
})
With above AngularJS code, I received following error:
XMLHttpRequest cannot load http://api.duckduckgo.com/?q=computer&format=json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:63342' is therefore not allowed access.
AngularJS uses JQuery so I tried the same in JQuery with following code:
var url="http://api.duckduckgo.com/?q=computer&format=json";
$.getJSON(url , function( data ) {
console.log(data);
});
This also produced the same error as did AngularJS code.
Then my further research brought me to the point that it's actually not specific to JQuery and AngularJS. Both of these inherit this issue from Javascript!
Here is an excellent resource with explanation of what CORS is and how to handle with it: http://enable-cors.org/index.html.
And also W3C has it official CORS specification: http://www.w3.org/TR/cors/
So my question is not what CORS is. My question is
My understanding is that whether it is a web browser or it is PHP/RoR or it is Javascript frameworks, all make requests to a URL via the same http or https, right? Certainly, yes. Then why http has to be more secure when requests come from javascript? How does http and server know that request is coming from javascript?
When a web browser can open a URL and PHP/RoR (or any server-side language) can access that URL without any extra settings/headers, why can't AngularJS, JQuery (or in a single word javascript) access that URL unless the server has set Access-Control-Allow-Origin header for requesting root?
What's that special feature (that PHP/RoR have and) that is missing in Javascript so that it can't access the same URL in the same browsers that can open that URL without any issue from their address bars?
Just to mention that I am basically an iOS developer and recently started to learn web development, specially AngularJS. So I am curious about what's all this going on and why!
It's disabled from javascript for security reasons. Here's one scenario:
Assume Facebook has a "post message on timeline" api that requires the user to be authenticated.
You are logged into Facebook when you visit badsite.com.
badsite.com uses javascript to call the Facebook api. Since the browser is making a valid request to Facebook, your authentication cookie is sent, and Facebook accepts the message and posts badsite's ad on your timeline.
This isn't an issue from a server, because badsite.com's server doesn't have access to your Facebook authentication cookie and it can't forge a valid request on your behalf.
You remember that all javascript request is handled by browser. So browser detect cross-origin request is easy.
Request from javascript has no difference with PHP/RoR, it is only rejected by browser.
Server code can accept cross-origin javascript request by header "Access-Control-Allow-Origin" because before reject javascript request, browser will send a request "OPTIONS" to server to ask header "Access-Control-Allow-Origin" on response. If value is match with current origin, browser will accept javascript request and send to server.
All browser are implement this policy Same Origin Policy
Please read http://en.wikipedia.org/wiki/Cross-site_scripting, you will get the reason why its prohibited for JavaScript.

Categories