How do REST APIs work with JavaScript when the same-origin policy exists for browsers? - api

I am working with Flickr's REST API and it's working fine. By that, I mean I'm making an AJAX call to the Flickr API and getting a JSON object back, parsing the object, etc. etc.
But this raises a question in my mind. If browsers follow the same-origin policy, then how can they make these types of API requests?
This DEMO fiddle is working, but it sends a cross-domain request to the Flickr domain.
How does this cross-domain request work?
The cross-domain request:
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?id=" +
id + "&lang=en-us&format=json&jsoncallback=1");

What you need to understand is that, while browsers do enforce the same origin policy (SOP), there are exceptions when SOP is not enforced. For example, if you have an HTML page - you can insert <img> tags that point to images on any domain. Therefore, the SOP doesn't apply here and you are making a cross-origin HTTP GET request for an image.
The demo you linked to works since it uses a mechanism that works in a similar way. The mechanism is called JSONP - http://en.wikipedia.org/wiki/JSONP and I suggest you read the wiki entry and some other blog posts. In essence, JSONP dynamically injects <script> tags to send a request to any domain (the parameters of the request are added as URL query params), since the same origin policy does not apply to <script> tags (as it doesn't apply to <img> tags).
Another way to invoke REST APIs on other domains is to use the cross-origin resource sharing mechanism (CORS) - http://en.wikipedia.org/wiki/Cross-origin_resource_sharing. In essence, this mechanism enables that browsers don't deny cross-origin request, but rather ask the target service if it wants to allow a specific cross-origin request. The target service tells the browser that it wants to allow cross-origin requests by inserting special HTTP headers in responses:
Access-Control-Allow-Origin: http://www.example.com

Related

Firebase auth breaks with cross origin isolation (i.e. when using Cross-Origin-Resource-Policy)

I am trying to make a website cross origin isolated, and enabled the following headers on my site:
https://web.dev/cross-origin-isolation-guide/
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Firebase auth uses a call to:
https://<AUTH_DOMAIN>/__/auth/iframe?apiKey=<API_KEY>&appName=[DEFAULT]
This gets blocked if you and makes authentication fail.
Because your site has the Cross-Origin Embedder Policy (COEP) enabled, each resource must specify a suitable Cross-Origin Resource Policy (CORP). This behavior prevents a document from loading cross-origin resources which don’t explicitly grant permission to be loaded.
To solve this, add the following to the resource’s response header:
Cross-Origin-Resource-Policy: same-site if the resource and your site are served from the same site.
Cross-Origin-Resource-Policy: cross-origin if the resource is served from another location than your website. ⚠️If you set this header, any website can embed this resource.
How does one fix this? It seems like the root issue is that firebase needs to set a header on their side ?

S3 CORS policy for public bucket

It seems to be easy, but I don't know what I am missing.
I have a public bucket with a js script that I fetch from my web site. I noticed that I don't send Origin header to S3, it is not required and everything works without any CORS configurations.
What's more, even after I manually added Origin header to that GET call and explicitly disallowed GET and my domain via:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>http://www.nonexistingdomain.com</AllowedOrigin>
<AllowedMethod>POST</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
I can still get the content. What's going on here?
Ok, after a conversation with Quentin, I think I understand where I am misinterpreting how CORS should work.
In Java world, it's a very common practice to actually reject requests when Origin doesn't match. Here is another thread where it's mentioned.
If we take Spring as an example(which is de-facto standard in Java world), here is what happens when CORS filter is added:
String allowOrigin = checkOrigin(config, requestOrigin);
...
if (allowOrigin == null) {
logger.debug("Reject: '" + requestOrigin + "' origin is not allowed");
rejectRequest(response);
return false;
}
where:
/**
* Invoked when one of the CORS checks failed.
*/
protected void rejectRequest(ServerHttpResponse response) {
response.setStatusCode(HttpStatus.FORBIDDEN);
}
You can find the code here.
But to my surprise, it is not such a common practice with other stacks and server-side technologies. Another common approach would be to send whatever CORS configuration they have to the browser and leave the decision to it.
S3 is even more tricky: it only sends CORS response headers when the bucket CORS rules match the CORS-enabled request(a request qith Origin header). Otherwise, there would be no CORS response headers.
The Same Origin Policy is a feature enforced by browsers which prevents JavaScript running on one website from reading data from a different website. (This stops random websites using JavaScript to use your browser to skip past your corporate firewall and access your intranet or read your GMail with your cookies).
CORS lets a website relax the Same Origin Policy to allow other websites to read data from it that way.
CORS is not authentication/authorisation. Your public bucket is public.
You aren't using JavaScript to read data from your bucket, you are loading the JS directly from the bucket.
Let's break down the problem and try to understand the fundamentals of CORS.
What is Cross-Origin Request & CORS?
Cross-Origin Request: A request for a resource (like
an image or a font) outside of the origin is known as a cross-origin
request.
CORS is helpful when you are requesting for a protected resource from another origin.
Cross-Origin Request Sharing: A request for a protected resource (like an image or a font or an XHR request) outside of the origin is known as a cross-origin request.
Why do we need CORS when the resources can be protected by using authentication/authorization tokens?
CORS is the first line of defense. When both the client (e.g., browsers) and servers are CORS-aware, clients will allow only requests from the specific origins to the servers as per instructed by the servers.
By default, browsers are supposed to implement same-origin policy security mechanism as per the guidelines on building the browser. Almost all the modern browser implement same-origin policy that instructs the browsers to allow requests to the servers if the origin is the same.
Same-origin policy is a security mechanism of a browser, you can read more about it here. It is because of this feature of browsers, the browser blocks all the request when the designation origin and the source origin are different. (Servers are not even aware that this is happening, Wow!)
For simpler use cases, when the assets(js, CSS, images, fonts), XHR resources are accessible with the same origin, there is no need to worry about CORS.
If assets are hosted on another origin or XHR resource are hosted on servers with a different domain that the source then browsers will not deny the request to cross-origin by default. Only with appropriate CORS request and response headers, browsers are allowed to make cross-origin requests.
Let's look at the request and response headers.
Request headers
Origin
Access-Control-Request-Method
Access-Control-Request-Headers
Response headers
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Expose-Headers
Access-Control-Max-Age
Access-Control-Allow-Methods Access-Control-Allow-Headers
For setting up CORS the Origin, and Access-Control-Allow-Origin headers are needed. Browsers will automatically add Origin header to every request, so a developer needs to configure only Access-Control-Allow-Origin response header.
For protecting access to resources only from specific domains, S3 provides an option to configure CORS rules. If the value of Access-Control-Allow-Origin header is * all the cross-origin requests are allowed, or else define a comma-separated list of domains.
There are a couple of things you need to be aware of when using CORS.
It's the first level of defense to a protected resource and not the ultimate defense.
You still need to implement appropriate authentication & authorization for the resource to perform CRUD operations on the server.
Implementing Same Origin Policy is a guideline for building the browser and are not mandatory.
CORS headers are useful only when clients accept the headers. Only modern browsers accept CORS headers. If you are not using browsers to make the resource request, then CROS do not apply.
If you type the link in the address bar of the browser, the CORS rules are not applied to because the browser does not send the Origin header to the server. The Origin header is sent by the browser only on the subsequent resource request (stylesheets, js files, fonts) and XHR requests by the origin.
If you access the resource file by directly typing the link in the address bar, the browser does not send Origin header to that request.
Also, if you want to restrict GET access use S3 pre-signed URL on a private bucket.

Difference between CORS and HTTP requests to a 3rd party API

Are GET requests considered CORS since the request is obtaining resources from another domain?
I get from Wikipedia that CORS is:
"a mechanism that allows restricted resources (e.g. fonts) on a web page to be requested from another domain outside the domain from which the resource originated."
However given this example:
$.ajax({
type: "GET",
url: "https://api.twitch.tv/kraken/base",
success: function(data){
//do stuff
}
});
If www.mywebsite.com (a domain) made the above GET request to Twitch (a domain outside the domain) does this count as CORS?
A HTTP request is a HTTP request.
Normally when you're on one domain, you are restricted to do stuff on another domain. This is called the browser sandbox.
CORS is a way for third party domains (twitch in your case) to grant you access after all.
In the particular example you state, twitch will indeed need to set the appropriate CORS headers to give you access. If twitch doesn't do this, you will not be able to read data.
Note that your code sample is horribly broken.

Why token-based authentication is better than cookie-based authentication in CORS/Cross-domain scenario?

I have seen in many places that one of the benefits of token-based authentication over cookie-based authentication is it is better suite for CORS/cross-domain scenario.
But why?
Here is a CORS scenario:
An HTML page served from http://domain-a.com makes an <img> src
request for http://domain-b.com/image.jpg.
Even if there's a token on my machine, how could the mere <img> tag know where to find and send it?
And according to here, it is suggested to store JWT as cookie, so how could that survive the CORS/cross-domain scenario?
ADD 1
Token-based authentication is easier to scale out than session-cookie one. See a related thread here: Stateless web application, an urban legend?
Just for clarification: Requests to any subdomain that you have are also considered as cross origin request (ex. you make request from www.example.com to api.example.com).
A simple <img> GET request to another origin is, indeed, cross origin request as well, but browsers are not using preflighted (OPTION) requests if you use GET, HEAD, POST requests only, and your Content-Type header is one of the followings:
application/x-www-form-urlencoded
multipart/form-data
text/plain
Therefore simple <img> request to another origin won't have problem (regardless if subdomain or totally another domain), as it won't go through preflight, unless it requires credentials, because when you add Authorization header, the request needs to go through preflight.
About storing in localstorage vs in cookie: Localstorage has single origin policy, meaning you cannot access the data you have stored from the subdomain, ie, example.com cannot access data in localstorage of api.example.com. On the other hand, using cookies, you can define which subdomains can access to the cookie. So you can access your token in stored in cookie and send it to server with your requests. Cookies also doesn't allow to access data across different domains.
Hope this helps.

Get an API key from external site

I have a site, which uses where2GetIt API. I need to get some data from this site,
but after making request, I get "No 'Access-Control-Allow-Origin' header is present on the
requested resource. Origin 'null' is therefore not allowed access" error.
I assume, that the problem is, that I have not access to API. How can I get the access to API?
If you're doing this with Javascript, make sure you understand CORS
...CORS gives web servers cross-domain access controls, which enable
secure cross-domain data transfers. Modern browsers use CORS in an API
container - such as XMLHttpRequest - to mitigate risks of cross-origin
HTTP requests.
See also this StackOverflow question How does Access-Control-Allow-Origin header work?