Is there a way to avoid Preflighting with $http? - angularjs

I'm using nginx on the remote server, and with no support for the OPTIONS method I've been terribly stuck. Both the server and angular refuse to talk to each other.
I just want to make a simple $http.post() request. Is there a way to configure the service to ONLY send the POST request, and not do any preflighting with OPTIONS?

This is not something AngularJS does, but something your browser does according to the Cross-Origin Resource Sharing standard. See also this answer on a related issue.
However, if you make it so that the AngularJS application is served from the same domain as your resource (different subdomains will affect cross-origin), then the browser will not send the OPTIONS request as the resource is no longer from a cross-origin server.
Example:
www.example.com requests resource on api.example.com will trigger OPTIONS request
www.example.com requests resource from www.example.com/api will not trigger OPTIONS request

If CORS is Unavoidable
You could change the header of the request to text/plain and then parse your response manually acording to answers in this link below
How to skip the OPTIONS preflight request in AngularJS

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.

Allowing CORS only for my domain?

I have an AngularJs website and when I am trying to post data then when I am opening my website without using www then I am getting
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource
Otherwise, I am not getting any error.
I tried to search and found that I should implement CORS on my backend which is in NodeJs but can anyone please tell me how can I only implement CORS Headers such that for both www and without, it would work but for any other domain trying to access my API must result in preflight error.
I am trying to do this because I read here which-security-risks-do-cors-imply that allowing all domains can increase security overhead for my website which I do not want.
Thanks.
I'm afraid this is not something you can tweak just in your client-side code. In order for cross-origin requests to work, you need to set an http response header: it's the server, who serves the resource, who will need the change, not the client side code from angularJs.
I believe that you should update your question stating what your server side language is and how are you handling http requests in the server side. As far as I know, just adding a header like:
Access-Control-Allow-Origin: http://client.domain.com
In your responses will do the trick. Where client.domain.com is the domain of your client, angularJs application.

CORS issue between web/android/ios

when trying to $.ajax to fetch some content from other websites in my website, I got the error.
Failed to load https://www.pinterest.com/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8100' is therefore not allowed access.
I knew if the target website didn't allow localhost:8100 to fetch the data, I cannot fetch it in the client side on the web.
However, I found that mobile app (not mobile browser, but android/ios application) does not have the issue, they can simply get the website content by their default mobile built-in HTTP get function.
Do i want to ask why mobile will not encounter CORS issue (mobile can fetch the webcontent simply by the built-in http get function)?
thanks.
CORS is enforced by the browser to fulfill the security standard they have to meet. It does not affect requests made programmatically from any language, like a curl call on bash.
This is how CORS works, based on Wikipedia:
The browser sends the OPTIONS request with an Origin HTTP header. The value of this header is the domain that served the parent page. When a page from http://www.example.com attempts to access a user's data in service.example.com, the following request header would be sent to service.example.com: Origin: http://www.example.com.
The server at service.example.com may respond with:
An Access-Control-Allow-Origin (ACAO) header in its response indicating which origin sites are allowed. For example Access-Control-Allow-Origin: http://www.example.com
An error page if the server does not allow the cross-origin request
An Access-Control-Allow-Origin (ACAO) header with a wildcard that allows all domains: Access-Control-Allow-Origin: *
The way CORS works means it is optional. Browsers enforce it to prevent Javascript AJAX calls to perform malicious calls. But other types of consumers built by hand don't need to enforce CORS.
Think in this example:
You are the owner of somesite.com
Users authenticate to your site using the traditional cookie method
User logins into anothersite.com, built by an attacker. This site has the following code:
<script>fetch('http://somesite.com/posts/1', { method: 'DELETE' });</script>
... effectively performing a request to your site and doing bad things.
Happily, the browser will perform a preflight request when it sees a cross-domain request, and if your site does not respond saying that requests coming from anothersite.com are OK, you will be covered by default from a potential attack
This is why CORS only makes sense in the context of a browser. Javascript you send to the browser can not (at least easily) circumvent CORS because the only API that allows you to perform requests from the browser is written in stone. Additionally, there are no local storage or cookies outside of the browser.
Corolarium: Enforcing CORS is a deliberate action from the requester, or whoever is making the requests for you, not the sender. Javascript APIs in browsers enforce it. Other languages don't have the need for the reasons explained.
When running on a device, your files are served over the file:// protocol, not http://, and your origin will therefore not exist. That's why the request from the native device does not trigger CORS.

$http.post cross domain request not working in Internet explorer (Network Error 0x80070005, Access is denied)

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.

docker hub api giving No 'Access-Control-Allow-Origin' error

I'm trying to fetch the list of official images from docker hub using v2 api. If I try to do curl or use postman, I get the response correctly, but when I try to fetch the list using angularjs service, I get the following error
XMLHttpRequest cannot load https://hub.docker.com/v2/repositories/library/?page=8&page_size=15. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://run.plnkr.co' is therefore not allowed access.
Can someone suggest solution for this. How can I enable cors for this?
CORS could be enabled on the server side, and this is not your case. What you could do is :
1) use a proxy, for instance NGNIX, and make Sure that all request Made to localhost/whatever are redirected to hub.docker.com . This way you can "cheat" Cross-origin block
2) if you need a temporary and dirty solution you could more simply install chrome/safari plugins to bypass CORS security check
There is only one way to bypass CORS is send request through a cors proxy like http://crossorigin.me
It's an opensource project and you can build your own proxy server by download the full source code from here: https://github.com/technoboy10/crossorigin.me
Reason behind the issue :
As per my understanding you are doing an AJAX call to a different domain than your page is on. So, the browser is blocking it for security reasons as it usually allows a request in the same origin.A tutorial about how to achieve that is using CORS.
When you are using curl or postman they are not restricted by this policy. Quoted from Cross-Origin XMLHttpRequest:
Regular web pages can use the XMLHttpRequest object to send and receive data from remote servers, but they're limited by the same origin policy. Extensions aren't so limited. An extension can talk to remote servers outside of its origin, as long as it first requests cross-origin permissions.

Resources