Access-Control-Allow-Origin error but request goes through - angularjs

I'm currently deploying a basic API to my live server and I'm running into (what I think is) a CORS problem but there is some behavior going on that I can't explain.
I'm communicating from an AngularJS front-end to a Laravel 5 (+ laravel-cors) back-end.
I started testing with a simple jQuery AJAX call (below) and when I make a request from my local Vagrant environment (http://dev.example.local/test.html) to http://api.example.com/v1/matches I get an error about Access-Control-Allow-Origin. The weird thing is that the request does come through because the information is stored in the database via the Laravel-based API correctly.
$.ajax({
method: 'POST',
url: 'http://api.example.com/v1/players',
data: {
"username": "username",
"first_name": "First",
"last_name": "Last",
"nickname": ""
}
}).always(function(r) {
console.log(r);
});
Error:
XMLHttpRequest cannot load http://api.example.com/v1/players. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://other.example.com' is therefore not allowed access.
The console.log(r) returns {readyState: 0, responseJSON: undefined, status: 0, statusText: "error"}
I developed the application locally using a Homestead VM (API) and a Vagrant environment (application) and it's working correctly within these environments...
Some observations:
Each of these requests shows up with Method: POST, Status: 200 OK, Type: xhr in my Chrome Developer Tools.
Tools like Postman and PhpStorm's RESTful service tester correctly execute the request and the data is added without errors.
Any ideas on how to further debug this problem are welcome... I've been trying to wrap my head around this for the entire day now and I just don't know what's causing it.

Your server must return an appropriate Access-Control-Allow-Origin header in the response. For example, if the request is being sent from http://stackoverflow.com, then your server must return this header: Access-Control-Allow-Origin: http://stackoverflow.com. You can determine, server-side, what the origin is by looking at the Origin header on the request. If your server does not return this header in the response, you will not have any access to the properties of the response browser-side (such as the status code, headers, or message body). The Same Origin Policy is at the center of this restriction.
The reason you are not seeing any similar issues when the request is sent by Postman or PhpStorm's RESTful service tester is due to the fact that these services do not send an Origin header with the request, as they are not subject to the Same Origin policy. By default, the browser will append this header to any cross-origin ajax requests, as browsers are subject to the Same Origin Policy. In my previous scenario, the request header would look like this: Origin: http://stackoverflow.com. Browsers that implement the CORS spec are required to add this request header so the server is able to determine if the origin of the request has been whitelisted for cross-origin ajax requests. If this is the case, the server will return the proper Access-Control-Allow-Origin header. If not, it can simply omit the header. Browsers that do not implement the CORS spec will simply refuse to send such an ajax request.
Regarding your bewilderment as to why the request is being sent in the first place, that comes down to a distinction between "simple" and "non-simple" CORS requests. For simple CORS requests, the request will always be sent to the server, but the client/JS will not be able to parse the response without proper acknowledgement from the server. Some CORS requests are not simple, so to speak. These are, for example, DELETE or PATCH requests, or POST/GET requests that contain non-standard headers (such as X-headers or a Content-Type of "application/json" as opposed to "multipart/form-data"). In other words, a request is not simple if it cannot be sent without JavaScript. For example, a <form> submit, or a GET request from a <script src="..."> will always send "simple" requests. For non-simple requests, the browser must "preflight" the request. This means that the browser sends an intermediate request, called a preflight, before the original request. This preflight request is an OPTIONS request. The server must than return headers in the response to this preflight that acknowledge any non-standard properties of the original request. If it does, then the browser will send the original request.
You can read more about preflighting and CORS in general on MDN.

Related

When OPTION / preflight is called, and when it is not? [duplicate]

I am building a web API. I found whenever I use Chrome to POST, GET to my API, there is always an OPTIONS request sent before the real request, which is quite annoying. Currently, I get the server to ignore any OPTIONS requests. Now my question is what's good to send an OPTIONS request to double the server's load? Is there any way to completely stop the browser from sending OPTIONS requests?
edit 2018-09-13: added some precisions about this pre-flight request and how to avoid it at the end of this reponse.
OPTIONS requests are what we call pre-flight requests in Cross-origin resource sharing (CORS).
They are necessary when you're making requests across different origins in specific situations.
This pre-flight request is made by some browsers as a safety measure to ensure that the request being done is trusted by the server.
Meaning the server understands that the method, origin and headers being sent on the request are safe to act upon.
Your server should not ignore but handle these requests whenever you're attempting to do cross origin requests.
A good resource can be found here http://enable-cors.org/
A way to handle these to get comfortable is to ensure that for any path with OPTIONS method the server sends a response with this header
Access-Control-Allow-Origin: *
This will tell the browser that the server is willing to answer requests from any origin.
For more information on how to add CORS support to your server see the following flowchart
http://www.html5rocks.com/static/images/cors_server_flowchart.png
edit 2018-09-13
CORS OPTIONS request is triggered only in somes cases, as explained in MDN docs:
Some requests don’t trigger a CORS preflight. Those are called “simple requests” in this article, though the Fetch spec (which defines CORS) doesn’t use that term. A request that doesn’t trigger a CORS preflight—a so-called “simple request”—is one that meets all the following conditions:
The only allowed methods are:
GET
HEAD
POST
Apart from the headers set automatically by the user agent (for example, Connection, User-Agent, or any of the other headers with names defined in the Fetch spec as a “forbidden header name”), the only headers which are allowed to be manually set are those which the Fetch spec defines as being a “CORS-safelisted request-header”, which are:
Accept
Accept-Language
Content-Language
Content-Type (but note the additional requirements below)
DPR
Downlink
Save-Data
Viewport-Width
Width
The only allowed values for the Content-Type header are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
No event listeners are registered on any XMLHttpRequestUpload object used in the request; these are accessed using the XMLHttpRequest.upload property.
No ReadableStream object is used in the request.
Have gone through this issue, below is my conclusion to this issue and my solution.
According to the CORS strategy (highly recommend you read about it) You can't just force the browser to stop sending OPTIONS request if it thinks it needs to.
There are two ways you can work around it:
Make sure your request is a "simple request"
Set Access-Control-Max-Age for the OPTIONS request
Simple request
A simple cross-site request is one that meets all the following conditions:
The only allowed methods are:
GET
HEAD
POST
Apart from the headers set automatically by the user agent (e.g. Connection, User-Agent, etc.), the only headers which are allowed to be manually set are:
Accept
Accept-Language
Content-Language
Content-Type
The only allowed values for the Content-Type header are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
A simple request will not cause a pre-flight OPTIONS request.
Set a cache for the OPTIONS check
You can set a Access-Control-Max-Age for the OPTIONS request, so that it will not check the permission again until it is expired.
Access-Control-Max-Age gives the value in seconds for how long the response to the preflight request can be cached for without sending another preflight request.
Limitation Noted
For Chrome, the maximum seconds for Access-Control-Max-Age is 600 which is 10 minutes, according to chrome source code
Access-Control-Max-Age only works for one resource every time, for example, GET requests with same URL path but different queries will be treated as different resources. So the request to the second resource will still trigger a preflight request.
Please refer this answer on the actual need for pre-flighted OPTIONS request: CORS - What is the motivation behind introducing preflight requests?
To disable the OPTIONS request, below conditions must be satisfied for ajax request:
Request does not set custom HTTP headers like 'application/xml' or 'application/json' etc
The request method has to be one of GET, HEAD or POST. If POST, content type should be one of application/x-www-form-urlencoded, multipart/form-data, or text/plain
Reference:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
When you have the debug console open and the Disable Cache option turned on, preflight requests will always be sent (i.e. before each and every request). if you don't disable the cache, a pre-flight request will be sent only once (per server)
Yes it's possible to avoid options request. Options request is a preflight request when you send (post) any data to another domain. It's a browser security issue. But we can use another technology: iframe transport layer. I strongly recommend you forget about any CORS configuration and use readymade solution and it will work anywhere.
Take a look here:
https://github.com/jpillora/xdomain
And working example:
http://jpillora.com/xdomain/
For a developer who understands the reason it exists but needs to access an API that doesn't handle OPTIONS calls without auth, I need a temporary answer so I can develop locally until the API owner adds proper SPA CORS support or I get a proxy API up and running.
I found you can disable CORS in Safari and Chrome on a Mac.
Disable same origin policy in Chrome
Chrome: Quit Chrome, open an terminal and paste this command: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
Safari: Disabling same-origin policy in Safari
If you want to disable the same-origin policy on Safari (I have 9.1.1), then you only need to enable the developer menu, and select "Disable Cross-Origin Restrictions" from the develop menu.
As mentioned in previous posts already, OPTIONS requests are there for a reason. If you have an issue with large response times from your server (e.g. overseas connection) you can also have your browser cache the preflight requests.
Have your server reply with the Access-Control-Max-Age header and for requests that go to the same endpoint the preflight request will have been cached and not occur anymore.
I have solved this problem like.
if($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && ENV == 'devel') {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: X-Requested-With');
header("HTTP/1.1 200 OK");
die();
}
It is only for development. With this I am waiting 9ms and 500ms and not 8s and 500ms. I can do that because production JS app will be on the same machine as production so there will be no OPTIONS but development is my local.
You can't but you could avoid CORS using JSONP.
After spending a whole day and a half trying to work through a similar problem I found it had to do with IIS.
My Web API project was set up as follows:
// WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
//...
}
I did not have CORS specific config options in the web.config > system.webServer node like I have seen in so many posts
No CORS specific code in the global.asax or in the controller as a decorator
The problem was the app pool settings.
The managed pipeline mode was set to classic (changed it to integrated) and the Identity was set to Network Service (changed it to ApplicationPoolIdentity)
Changing those settings (and refreshing the app pool) fixed it for me.
OPTIONS request is a feature of web browsers, so it's not easy to disable it. But I found a way to redirect it away with proxy. It's useful in case that the service endpoint just cannot handle CORS/OPTIONS yet, maybe still under development, or mal-configured.
Steps:
Setup a reverse proxy for such requests with tools of choice (nginx, YARP, ...)
Create an endpoint just to handle the OPTIONS request. It might be easier to create a normal empty endpoint, and make sure it handles CORS well.
Configure two sets of rules for the proxy. One is to route all OPTIONS requests to the dummy endpoint above. Another to route all other requests to actual endpoint in question.
Update the web site to use proxy instead.
Basically this approach is to cheat browser that OPTIONS request works. Considering CORS is not to enhance security, but to relax the same-origin policy, I hope this trick could work for a while. :)
you can also use a API Manager (like Open Sources Gravitee.io) to prevent CORS issues between frontend app and backend services by manipulating headers in preflight.
Header used in response to a preflight request to indicate which HTTP headers can be used when making the actual request :
content-type
access-control-allow-header
authorization
x-requested-with
and specify the "allow-origin" = localhost:4200 for example
One solution I have used in the past - lets say your site is on mydomain.com, and you need to make an ajax request to foreigndomain.com
Configure an IIS rewrite from your domain to the foreign domain - e.g.
<rewrite>
<rules>
<rule name="ForeignRewrite" stopProcessing="true">
<match url="^api/v1/(.*)$" />
<action type="Rewrite" url="https://foreigndomain.com/{R:1}" />
</rule>
</rules>
</rewrite>
on your mydomain.com site - you can then make a same origin request, and there's no need for any options request :)
It can be solved in case of use of a proxy that intercept the request and write the appropriate headers.
In the particular case of Varnish these would be the rules:
if (req.http.host == "CUSTOM_URL" ) {
set resp.http.Access-Control-Allow-Origin = "*";
if (req.method == "OPTIONS") {
set resp.http.Access-Control-Max-Age = "1728000";
set resp.http.Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, PATCH, OPTIONS";
set resp.http.Access-Control-Allow-Headers = "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since";
set resp.http.Content-Length = "0";
set resp.http.Content-Type = "text/plain charset=UTF-8";
set resp.status = 204;
}
}
What worked for me was to import "github.com/gorilla/handlers" and then use it this way:
router := mux.NewRouter()
router.HandleFunc("/config", getConfig).Methods("GET")
router.HandleFunc("/config/emcServer", createEmcServers).Methods("POST")
headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOk := handlers.AllowedOrigins([]string{"*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
log.Fatal(http.ListenAndServe(":" + webServicePort, handlers.CORS(originsOk, headersOk, methodsOk)(router)))
As soon as I executed an Ajax POST request and attaching JSON data to it, Chrome would always add the Content-Type header which was not in my previous AllowedHeaders config.

CORS Error happens in api-key request in react js

I want to get api_kye and I use moqui framework in backend , use axios in react js project :
axios.get(SERVER_URL + '/rest/api_key', {
headers: {
Authorization: "Basic " + btoa(username + ":" + password) ,
'Content-Type' : 'application/x-www-form-urlencoded' ,
},
}).then(response => {})
then , when requested the below error happened :
Access to XMLHttpRequest at '...' from origin '...' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header has a value '...' that is not equal to the supplied origin.
This is not a reactJS error, this is a problem with your backend code, you need to look at the Moqui docs to see how you can allow the origin you are calling from to access your API
Enable CORS in the server side
Let's head back to our server's app.js file.
app.get('/cors', (req, res) => {
res.set('Access-Control-Allow-Origin', '*');
res.send({ "msg": "This has CORS enabled 🎈" })
});
Also check the redirect URL in server side
Refer this link
when ever you send an HTTP request to an API the server response should include your domain in response header otherwise chrome will raise this error: "Access to XMLhttprequest has blocked for origin ...."
so first of all make sure that your domain is included in server code.
if it's included already then the reason could be that server can't process your response correctly. maybe it crashed so the response is corrupted and chrome can't find your domain in response header and it raises that error
So i have a solution for this but you may or may not be able to build it.
CORS is a security feature, you should be receiving requests thru your backend and not the browser directly in general...
IF the data is not sensitive and you want to open an endpoint to the world without getting CORS errors you can do one of two things
Set the CORS headers from the server side. You'll need to understand HTTP requests and headers. you set these from the server. enabling cross origin with * will work.
Build a proxy. I've done this in AWS API gateway, I'll link another post. works good. basically AWS will act as your back end and take the response with CORS blocked. you will then proxy the request and strip the CORS header. When you call the api you will actually call AWS which calls the API, then AWS will pass the response back to you with CORS enabled.
Angular No 'Access-Control-Allow-Origin'
just follow these steps and it will work.

Required headers and api version to make http requests to AWS service actions (Cloudwatch logs) createLogStream and putLogEvents without AWS-SDK?

To be precise, In order to make api requests to aws services over http, one must sign the requests using Sigv4 process(recommended by aws, described here https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html ). It has a sophisticated procedure in which the http request headers must be included in sign. I've added the essential headers for signing through Authorization header for createLogStream action of Cloudwatch logs service,
'Host': 'logs.<region>.amazonaws.com',
'Content-Type': 'application/x-amz-json-1.1',
'X-Amz-Date': <AMZ_DATE>,
'X-Amz-Target': Logs_20140328.CreateLogStream,
'X-Amz-Content-Sha256': <PAYLOAD_HASH>,
And making a Http request to https://logs.{region}.amazonaws.com/?Action=CreateLogStream&version={version} from the frontend application.
However, the following response error (400) occurs,
message: "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details."
__type: "InvalidSignatureException"
I have checked the secret access key and the signed headers, they were fine;
If I remove Content-Type header, I get a successful 200 response, but the response results in
{Output: {__type: "com.amazon.coral.service#UnknownOperationException", message: null}, Version: "1.0"}
One of the solutions to fix this issue recommends to add the Content-Type header (com.amazon.coral.service#UnknownOperationException when triggering CodeBuild from API Gateway).
Should Content-Type header be included? Would be great if any help on what headers must be included and the specific api version for Cloudwatch logs to be set ?
Thanks in advance!!!

CORS and ReactJS using Etsy APIs

I'm a n00b trying to learn React. I'm building a website that uses Etsy's APIs. Registered an app there and everything. They have a page that talks about CORS and proxying here: https://www.etsy.com/developers/documentation/getting_started/jsonp
So for example, I'm trying to do a fetch on a store's listings:
fetch('https://openapi.etsy.com/v2/shops/[shopId]/listings/active?api_key=[apiKey]')
But that violates the browser's CORS policy. No problem, says the documentation on Etsy. You can always use their proxy: beta-api.etsy.com. So I add that to my package.json file:
"proxy": "https://beta-api.etsy.com/"
And then I change my fetch line:
fetch('/v2/shops/[shopId]/listings/active?api_key=[apiKey]')
But CORS is still being violated in the browser.
Access to fetch at 'https://www.etsy.com/shop/beta-api/v2/shops/[shopId]/listings/active?api_key=[apiKey]' (redirected from 'http://localhost:3000/v2/shops/[shopId]/listings/active?api_key=[apiKey]') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I don't want an opaque response, either, so doing "no-cors" really doesn't help. Does anyone have any recommendations on how I can at least get a local website up and running?
You are getting that error because your server has set Access-Control-Allow-Origin to restrict cross-domain traffic. You are sending your request from your localhost so that is considered cross-domain.
If you are able to change your server settings to set Access-Control-Allow-Origin: * then you can make those requests without a CORS error but I don't suggest doing this.
The best solution would probably be a proxy server. You can use cors-anywhere heroku app to easily do this.
Excerpt from Medium Article on this issue
The cors-anywhere server is a proxy that adds CORS headers to a
request. A proxy acts as an intermediary between a client and server.
In this case, the cors-anywhere proxy server operates in between the
frontend web app making the request, and the server that responds with
data.
Similar to the Allow-control-allow-origin plugin, it adds the
more open Access-Control-Allow-Origin: * header to the response.
Say your frontend is trying to make a GET request to:
https://joke-api-strict-cors.appspot.com/jokes/random
But this api does not have a Access-Control-Allow-Origin value
in place that permits the web application domain to access it.
So instead, send your GET request to:
https://cors-anywhere.herokuapp.com/https://joke-api-strict-cors.appspot.com/jokes/random
The proxy server receives the
https://joke-api-strict-cors.appspot.com/jokes/random
from the url above. Then it makes the request to get that server’s
response. And finally, the proxy applies the Access-Control-Allow-
Origin: * to that original response.

Restangular prevent 'preflight' OPTIONS call

I'm trying to figure out how to prevent the OPTIONS call from firing on every GET call to our API server.
I'm trying this right now:
.config(function(RestangularProvider) {
RestangularProvider.setDefaultHeaders({"X-Requested-With" :"", "Content-Type": "text/plain"});
})
But it's not doing me any good. Everything still thinks it's application/json so it fires off the preflight call. Is there anything I can do?
Check this out:
OPTIONS requests are what we call pre-flight requests in Cross-origin
resource sharing (CORS).
They are necessary when you're making requests across different
origins.
This pre-flight request is made by some browsers as a safety measure
to ensure that the request being done is trusted by the server.
Meaning the server understands that the method, origin and headers
being sent on the request are safe to act upon.
Your server should not ignore but handle these requests whenever
you're attempting to do cross origin requests.
How to disable OPTIONS request?

Resources