Making Cross Domain Ajax Requests - ajax

Lets say, I have 2 web servers hosted at a.com and b.com b.com does NOT accept cross-domain requests from a.com.
IN one tab of my browser I have opened http://a.com and in another tab i have opened http://b.com.
As per Same Origin Policy, which of the following are true.
1) From http://a.com using ajax I can make cross-domain GET and POST request to http://b.com but can not read the response from http://b.com
2) From http://a.com using ajax I can make cross-domain GET and POST request to http://b.com and I can also read the response from http://b.com
3) From http://a.com using ajax I can make cross-domain GET, POST, PUT, DELETE request to http://b.com but can not read the response from http://b.com
4) From http://a.com using ajax I can make cross-domain GET, POST, PUT, DELETE request to http://b.com as well as read response from http://b.com
P.S. Not expecting any lectures on SOP and CORS. Short and sweet answers requested

The only true answer is 1. Simply because CORS preflight request fails on 3 and 4, so the actual request never even takes place.

Related

Normal to see Status 204 then Status 200?

I am looking at my ajax request in network tab in chrome and I noticed every ajax request I do happens twice.
First one is a 204 and then followed up with 200. My ajax call is only being hit once so I am not sure why there are 2.
Edit
So it seems to have to do with Cors, which I have just set to star (*) for testing.
I guess there is not to much I can do to not have it do 2 requests, but what really gets me is why it takes so long, I looking at google chrome network and on my page these 204 took anywhere from 110ms to 1.97 seconds.
That's a consequence of the CORS - Cross-origin resource sharing "protocol".
When doing requests do other domains, the browser do a request before your request, asking for the server if it can proceed with that request.
This request uses the method OPTIONS and should have no content, just response headers, that's why the response code is 204 (no content). After confirming that the request is allowed, the browser proceed with your request, that now will return 200 (or any other) status code.
When you try to send a AJAX request to a different domain, you are violating the same-origin policy.
Server that you are sending the request allows cross domain requests. In the process, there should be a preflight call and that is the HTTP OPTION call.
So, you are having two responses for the OPTION and your result.

Using Etags with ember-data

I am trying to implement some client side caching via etags and last modified headers; however, ember-data doesn't seem to be using the etags for the ajax requests. I only get 200 responses and never a 304. I don't see any ETag header being sent with the request headers.
When I make the ajax request multiple times directly from the browser address bar, I'll get 304 not modified responses after the first request, so I know the server is sending things back correctly.
Is there a configuration I need to set up to make this happen?
This is a CORS request, but I think that I have and I have exposed the ETag header:
Access-Control-Expose-Headers: ETag
and the Etag header is sent back with the response.

Angular resource how to keep ajax header and enable cors at the same time

In my ng-resource files, I enable the ajax header:
var app = angular.module('custom_resource', ['ngResource'])
app.config(['$httpProvider', function($httpProvider) {
//enable XMLHttpRequest, to indicate it's ajax request
//Note: this disables CORS
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
}])
app.factory('Article', ['$resource', function($resource) {
return $resource('/article/api/:articleId', {articleId: '#_id'}, {
update: {method: 'PUT'},
query: {method: 'GET', isArray: true}
})
}])
So that I can separate ajax and non-ajax request and response accordingly (to send json data like res.json(data), or to send the entire html page like res.render('a.html')
for example, in my error handler, I need to decide to render error.html page or to just send a error message:
exports.finalHandler = function(err, req, res, next) {
res.status(err.status || 500)
var errorMessage = helper.isProduction() ? '' : (err.message || 'unknown error')
if (req.xhr) {
res.json({message: errorMessage})
}
else {
res.render(dir.error + '/error_page.ejs')
}
}
But now I need to do CORS request to other sites. Is it possible to do CORS request while keeping the ajax header? or other ways I can identify ajax and non-ajax request from server?
In case my question is not clear, heres a relevant article about angular and CORS
http://better-inter.net/enabling-cors-in-angular-js/
Basically, we need to delete xhr header to enable cors for other server, but I need the header for my own server
EDIT 2:
today I tried integrating google map and I got this error:
XMLHttpRequest cannot load http://maps.googleapis.com/maps/api/geocode/json?address=Singapore&sensor=false. Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers.
Setting custom headers on XHR requests triggers a preflight request.
So, it doesn't disable CORS but your server is most likely not handling the preflight request.
Inspired from this post: https://remysharp.com/2011/04/21/getting-cors-working
The solution should be to use the cors module and add the following to your node.js code before your routes:
var corsOptions = {
origin: true,
methods: ['GET', 'PUT', 'POST'],
allowedHeaders: ['X-Requested-With','Content-Type', 'Authorization']
};
app.options('*', cors(corsOptions)); //You may also be just fine with the default options
You can read more at: https://github.com/expressjs/cors
you may try to use cors package
First, to address you primary concern is it possible to do CORS request while keeping the ajax header?: the answer is YES, provided the sites you are accessing allow requests from you or any other external clients at all.
You wrote:
//Note: this disables CORS
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
But I don't understand what you mean by, it "disables CORS". The X-Requested-With header is not a standard header, and the known effect of adding a non-standard header to a request (made from a browser) is the triggering of a pre-flight request [3].
If the other sites you are interested in would set their servers to refuse processing of requests that do not originate from their own domain, then whether you set that header or not, your request should fail.
It seems everything is working fine for you, for requests sent to you own server. Otherwise you can solve the problem by appending the Access-Control-Allow-Origin header in your server responses as follows:
if you need to allow requests from specific domains
response.set("Access-Control-Allow-Origin", "one-host-domain, your-host-domain, some-other-host-domain"); // second argument is a comma-delimited list of allowed domains
(It may be better for you to actually check the request object for the origin, and if it's permitted based on presence in a pre-determined list, then send back the exact same origin).
If you need to permit all requests regardless of its origin
response.set("Access-Control-Allow-Origin", "*");
That should do, and I hope it clears your doubts for you.
More info on handling CORS when using AJAX: 0, 1 & 2.
EDIT
Following exchanges in the comment, I add the following points to support this answer further.
As it is today, the only side that needs disabling/enabling CORS in the client-server system is the server. All modern browsers allow cross origin requests by default and you don't need to do anything additional to support that capability. I understood that you're adding a custom header to distinguish AJAX requests from the rest?? AFAIK, that header changes nothing about how requests are made by browsers.
Here is how all cross-origin requests are handled by browsers today: for all request methods (but usually with the exception of GET), browsers send a pre-flight request with the OPTION method. If the destination server allows it, the actual request is then sent, otherwise the request fails. In the case where the servers, responds with a refusal there's nothing you nor whatever library you use can do about it. This is the fact from my own experience.
There are 3 solutions that come to my mind:
1. Ask site's admin to enable x-requested-with header in CORS.
2. Use proxy server.
3. Send request without x-requested-with header.
This article should make it clear how CORS works and how to make CORS requests.
Particularly "Simple requests" section and "Access-Control" section, especially access-control-allow-headers description is important in this case.
As it says: for simple requests access-control-allow-origin is enough. However if the request includes custom header (a header which is not included by default, such as x-requested-with header), the preflight request is triggered, and server's response to this request should enable this custom header in access-control-allow-headers by setting its value to either "*" or to the name of a custom header (x-requested-with).
Hope it makes it a little bit clearer.

CORS-aided cross-origin-XHR

Modern browsers support CORS handily. If CORS-aided cross-origin-XHR is sent to CORS-ignorant site, the XHR succeeds in no question.
Does it expose more vulnerability in this regard? How to strictly enforce Same Origin Policy on today's browsers?
Take a look at how preflight requests work in CORS. The CORS preflight request protects servers from unauthorized requests by first asking the server whether it is ok to make the cross-origin request. If the server says "yes", the browser continues with the request. Otherwise the request fails.
Note that there are certain types of requests that don't need preflight requests. However, these requests were already possible even before CORS. For example, a simple GET request does not need a preflight, but a GET can already be made with a script tag.
You can learn more about CORS and the preflight here: http://www.html5rocks.com/en/tutorials/cors/

Send credentials in preflighted request (CORS)

I have to make CORS with content type JSON. This makes the browser send first an OPTIONS request, specifying origin and so on. Then the server should answer with allowed domains, methods, etc. If this goes well, the browser sends the actual request.
My problem is that the server needs authentication for the actual request but ALSO for the OPTIONS request. But the browser doesn't send the authentication headers with the OPTIONS request.
I'm using JQuery and the ajax() function. I tried adding "withCredentials: true", and add the Authorization header, but this not affect the OPTIONS request, it still doesn't send any credentials.
Any ideas? Thanks in advance.
The preflight request is only meant to verify if the CORS request itself is allowed. Therefore, cookies are never included in the preflight request. You'd have to validate the user during the actual request.
Actually, Chrome will send the cookies w/ the preflight request if withCredentials=true whereas Firefox will not. Sounds like they've implemented the spec differently.

Resources