Alright, first of all, I am absolutely aware that we have a bunch of answers on this and there is a plethora of articles on the topic. I just read these answers a second before typing this:
Why is CORS without credentials forbidden?.
Is CORS considered bad practice?
Etc. My particular situation is this - I just set up WebAPI2 for my practice project, the front end for which is running via gulp browser-sync. I have no idea how these ports get picked, but lets say the Web API is running on port http://localhost:1234/ and browser-sync generates the website on http://localhost:4321/. So I hit the API via angular's $http and get the famous CORS error (API controller method does get hit), so I am guessing it's the API returning not allowed. Edit: I fixed this via installing a CORS for Web API package via NuGet (Article Here) before asking this Q, just referencing for anyone who might need it later.
So, I was thinking, if I deployed this, ANY request would get denied, unless I am missing something. Or would it not be denied because of something I don't understand? Is disallowing CORS just a throwback from the MVC days? Or is there some purpose to it with APIs?
Maybe I am just ranting, but this confuses the **** out of me.
CORS is based on the response headers returned from the API. It is not the API that rejects responding to the request, the web browser explicitly disallows handling the response. The API will process the request as normal.
When dealing with anything other than a GET, CORS also requires a "preflight" request to the API first, to ensure subsequent requests are allowed. This amongst sending the headers back is what the Web API nuget package provides.
CORS is off by default for security purposes.
Related
I have a locally hosted wordpress installation using woocommerce, and a separate locally hosted react webapp that will be used to manage the products. I'm using the woocommerce-rest-api react plugin to call the end points.
GETs work fine and don't have any issues, however PUT and DELETE I'm having issues with CORS.
I've updated the wordpress htaccess:
and call the endpoint like so:
and this is what I get in dev tools:
Here is the preflight headers and response and then the failing call:
Any ideas what I'm doing wrong or what I've missed?
Your preflight response from (what i assume is the wordpress server) appears to be missing the Access-Control-Allow-Origin header in the response (sent back from the OPTIONS request).
To match your other requests this should be *. However it is not best practice to use * for security reasons and should instead use the domains you want to be able to access this from browsers.
I'm building a ReactJS frontend that has to gather some data from AWS Lambdas using a JS fetch.
I cannot make it work, no mater what CORS technique I apply. I've looked into other answers here to no avail.
I am definitely adding Access-Control-Allow-Origin with "*" value in my response (verified this using postman to call the endpoint). Also, Chrome complains about the preflight with Response to preflight request doesn't pass access control check, but no preflight request (OPTIONS method) is ever actually fired by chrome, all I see is the GET I'm trying to make on the first place, which is really confusing.
What am I missing? Why is chrome complaining about preflight when no OPTIONS preflight request is made? Why adding Access-Control-Allow-Origin with "*" in my response is not enough?
Thanks!
TL;DR: There was a preflight request happening, it just wasn't showing on chrome (there's a way to make them show up). Also, there's a tweak to make if you use custom headers for authorization tokens for example.
Summary
Well, after looking into this for a day and checking several other answers I'm posting this because none quite fit my problem, with the hope it will help anyone else facing this.
First, I'll summarize the several parts involved in the error and then how to fix it, without resorting to any "hackish" solution like bypassing CORS with a chrome extension, or using any 3rd party service, like many posts suggest. My setup looked like this:
ReactJS frontend, try to make a GET request using fetch javascript method, running on http://localhost:3000 for development
AWS Lambda Backend, that answers the GET with a JSON payload (coded in python, not important but anyways). This lambda is adding Access-Control-Allow-Origin:"*" header to its response.
The above AWS Lambda is behind what AWS calls an "Authorizer", which is a function that runs before your lambda to check whatever authorization header you want to use to protect access to your API. This is important as we'll see later, because due to some nonsense on AWS inner workings, sometimes you cannot use the standard HTTP Authorization header, and it defaults to using authorizationToken as they suggest in their documentation and samples (and changing it not always works, there are plenty of users reporting this in their forums). We'll keep a note on this for later.
Both API methods (actual API and its Authorizer) routed and published on the internet using AWS API Gateway, which in short is a way to pair your lambda with a public URL to call it from elsewhere.
Google Chrome used as browser (with its developer tools enabled to monitor things)
The error
When trying to call the lambda, chrome blocks the GET request with this error showing on the console: Response to preflight request doesn't pass access control check. My lambda is already answering with the correct Access-Control-Allow-Origin header so, what's wrong? Also, no preflight OPTIONS request are being made anyways, so this was confusing.
Some debugging
AWS Lambda is great but their debugging tools are not as fluid as I would like, so I replaced the lambda with a local expressjs server, implementing just two methods: GET /foo and OPTIONS /foo. To my surprise, when from my ReactJS frontend I fetched /foo, it did call OPTIONS /foo first (I confirmed this by adding logs to my endpoint, etc, something you can also do in lambdas but its not as easy).
What was actually happening
A "preflight" request is an OPTIONS request to validate what is actually allowed when doing the following GET, but the Network tab in Chrome was not showing any OPTIONS request actually happening (I remember they used to show up here). Well, they changed it at some point, and now they are hidden by default. If you want them to show again (as a developer, I do), you can re-enable that by changing the out-of-blink-cors flag to disabled as explained here.
After changing this flag, now the OPTIONS request does show on the network tab. From there I could craft the OPTIONS response so it would enable the required GET afterwards. There are other considerations when using credentials and other cases (I found this article from Mozilla helpful with that), but in short my OPTIONS response headers look like this:
Access-Control-Allow-Origin: "http://localhost:3000"
Access-Control-Allow-Methods: "GET, POST, OPTIONS"
Access-Control-Allow-Headers: "authorizationToken"
(That last one, Access-Control-Allow-Headers, comes into play when dealing with AWS Lambdas Authorizers. If you are using that custom header to send your tokens, you need to allow it here).
After making CORS work locally, to solve it for my lambdas I did two things:
You need your API Gateway to be able to answer OPTIONS request. There are a number of ways to achieve this, from writing your own lambda to answer it to having AWS to mock-response it for you. More info on that here.
You need to make sure your GET lambda adds the Access-Control-Allow-Origin header, pointing to the same value your OPTIONS response did (In my case, http://localhost:3000).
After that, all worked as expected.
Final Notes
I wrote this answer because I found the conjunction of "React-CORS-AWS-Authorization" was not actually covered by any questions I found around.
Also there are a number of problems that may arise from using localhost for development on chrome, leading to suggestions of using an external service like lvh.me, but this was not the case, and some answers misleadingly relate this CORS problem to that. Moreover, some answers suggest disabling CORS checks altogether with some chrome extension, which is really bad security advice.
Finally, I found the idea of making a simple expressJS server to debug the server-side of things pretty helpful in understanding what was happening, because sometimes you simply cannot access what's happening on the other side, so maybe this suggestion might help people shorten the time dealing with things like this.
When I make an $http.post request and set the "withCredentials" property to true.
My request works fine in Chrome and Fiefox. However, I'm getting the error below in IE:
XMLHttpRequest: Network Error 0x80070005, Access is denied.
I noticed that if I enable the "Access data resources across domains" setting in IE, The error gets resolved. However I need to find an alternative solution because I can't ask the users to enable that setting obviously.
I noticed that a $http.get request to the same domain is working in IE with no issue, the issue is only with the $http.post request, the Options request is getting a 500 internal server and I see the request and response headers below:
Note:
I do have the necessary custom headers, and I can see them in Chrome when the OPTIONS request succeeds. The headers that I see in Chrome are listed below:
Could you please let me know if I'm missing something that would make the request work in IE without having to enable Access data sources across domains?
Internet Explorer 9 doesn't support cookies in CORS requests. The withCredentials property of the $http arguments attempts to send cookies. I don't think there's any way to fix it with headers. IE10+ should work by default, just be sure that you are not in compatibility mode. CORS isn't fully implemented in IE10 either, but the type of request you are trying to do should work.
You didn't mention what the nature of your web app is, but it impacts the type of workaround you will need for IE9. If possible, see if you can refactor your code to use a GET request instead (again, I don't know what you are trying to do via AJAX so this may be impossible).
You may be able to use Modernizr or something similar to detect if the browser supports CORS. If it is not supported, send the request without AJAX and have a page refresh.
Another alternative if you really want to use AJAX is to set up a proxy on your web server, i.e. the server on the same domain. Instead of making the cross-origin request directly, you make the AJAX request to your same-origin server, which then makes the request to the cross-origin server for you. The server won't have CORS issues. This solution assumes, of course, that you have some server-side scripting going on such as PHP, Node or Java.
I have inherited a Ionic app which uses ng-token-auth+devise_token_auth to handle the authentication and the session between front and back.
What happens is quite strange. Sometimes (specially with slow connections) the request (or the response) get lost and after that I get only 401 http errors.
I know that that everytime I send a request the token expires, but when the xhr request is cancelled (by the server I suppose, or by the browser, I don't know) the token is expired without having been replaced by the new one generated by devise_token_auth gem.
I know Rails but I'm not familiar with Angular, neither Ionic and I don't know exactly where to look.
After reading a lot of SO answers where noone seems having my problem (which happens locally and in staging/production), I checked the following
storage is set as localStorage.
config.batch_request_buffer_throttle = 20.seconds
there is no pattern between cancelled requests, sometimes I perform get for the username, sometimes a post or a put to a comment.
Is not a CORS problem because it would happen always or never. (moreover I'm using a proxy as explained in ionic blog)
Maybe it could be related to provisional headers chrome bug. But, how can I be sure?
What puzzles my is that it happens only sometimes and not always. (and there are no errors in the backend)
The only workaround I have found in the devise_token_auth documentation is change config.change_headers_on_each_request to false avoiding in this way the regeneration of the token.
But I don't like this solution because I think it hides the real problem in an insecure way instead of solving the token loss. Any suggestion?
Kindly, please check this thing:
Version: which version of this gem (and ng-token-auth, jToker or Angular2-Token if applicable) are you using?
Request and response headers: these can be found in the "Network" tab of your browser's web inspector.
Rails Stacktrace: this can be found in the log/development.log of your API.
Environmental Info: How is your application different from the reference implementation?
This may include (but is not limited to) the following details:
Routes: are you using some crazy namespace, scope, or constraint?
Gems: are you using MongoDB, Grape, RailsApi, ActiveAdmin, etc.?
Custom Overrides: what have you done in terms of [custom controller overrides]
5?
Custom Frontend: are you using ng-token-auth, jToker, Angular2-
Token, or something else?
I have an AngularJS application that talks to various java services. In the application I have a global http header setting in an http interceptor. That means all the service requests from my application will get the header values.
Now the trouble is that all the services CORS settings won't allow this header value. Couple of services does, while others does not. The service calls to the servers that do not support the header fails, since the http interceptor always puts the header values.
Is there a better way to design, in the above said case, so as to avoid the issue stated?
Appreciate any help...
How about adding a response interceptor, looking for a 401 status? If you get a 401, attempt to do the same request again without the headers this time. If this succeeds, 'whitelist' this domain to make all following requests without the headers that you don't want.
Otherwise, if you have a limited number of services that you are making calls to, maybe whitelist them inside of your request interceptor? This would probably be easier, but it's not very elegant.