AngularJS not detecting Access-Control-Allow-Origin header? - angularjs

I am running an angular app on a local virtualhost (http://foo.app:8000). It is making a request to another local VirtualHost (http://bar.app:8000) using $http.post.
$http.post('http://bar.app:8000/mobile/reply', reply, {withCredentials: true});
In the Network tab of Chrome Developer Tools I of course see the OPTIONS request, and the response includes the header:
Access-Control-Allow-Origin: http://foo.app:8000
However, the POST request is cancelled with the following error:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://foo.app:8000' is therefore not allowed access.
Has anyone experienced this? The Access-Control-Allow-Origin header is very plainly included in the response of the OPTIONS request, so I can't for the life of me figure out why the POST is acting the header was missing.
Access-Control-Allow-Credentials is also set to true.

It's a bug in chrome for local dev. Try other browser. Then it'll work.

There's a workaround for those who want to use Chrome. This extension allows you to request any site with AJAX from any source, since it adds 'Access-Control-Allow-Origin: *' header to the response.
As an alternative, you can add this argument to your Chrome launcher: --disable-web-security. Note that I'd only use this for development purposes, not for normal "web surfing". For reference see Run Chromium with Flags.
As a final note, by installing the extension mentioned on the first paragraph, you can easily enable/disable CORS.

I was sending requests from angularjs using $http service to bottle running on http://localhost:8090/ and I had to apply CORS otherwise I got request errors like "No 'Access-Control-Allow-Origin' header is present on the requested resource"
from bottle import hook, route, run, request, abort, response
#https://github.com/defnull/bottle/blob/master/docs/recipes.rst#using-the-hooks-plugin
#hook('after_request')
def enable_cors():
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS, PUT'
response.headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept'

I experienced this exact same issue. For me, the OPTIONS request would go through, but the POST request would say "aborted." This led me to believe that the browser was never making the POST request at all. Chrome said something like "Caution provisional headers are shown" in the request headers but no response headers were shown. In the end I turned to debugging on Firefox which led me to find out my server was responding with an error and no CORS headers were present on the response. Chrome was actually receiving the response, but not allowing the response to be shown in the network view.

CROS needs to be resolved from server side.
Create Filters as per requirement to allow access and add filters in web.xml
Example using spring:
Filter Class:
#Component
public class SimpleFilter implements Filter {
#Override
public void init(FilterConfig arg0) throws ServletException {}
#Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response=(HttpServletResponse) resp;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
chain.doFilter(req, resp);
}
#Override
public void destroy() {}
}
Web.xml:
<filter>
<filter-name>simpleCORSFilter</filter-name>
<filter-class>
com.abc.web.controller.general.SimpleFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>simpleCORSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

I just ran into this problem today. It turned out that a bug on the server (null pointer exception) was causing it to fail in creating a response, yet it still generated an HTTP status code of 200. Because of the 200 status code, Chrome expected a valid response. The first thing that Chrome did was to look for the 'Access-Control-Allow-Origin' header, which it did not find. Chrome then cancelled the request, and Angular gave me an error. The bug during processing the POST request is the reason why the OPTIONS would succeed, but the POST would fail.
In short, if you see this error, it may be that your server didn't return any headers at all in response to the POST request.

It can also happen when your parameters are wrong in the request. In my case I was working with a API that sent me the message
"No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 401."
when I send wrong username or password with the POST request to login.

Instead of using $http.get('abc/xyz/getSomething') try to use
$http.jsonp('abc/xyz/getSomething')
return{
getList:function(){
return $http.jsonp('http://localhost:8080/getNames');
}
}

If you guys are having this problem in sails.js just set your cors.js to include Authorization as the allowed header
/***************************************************************************
* *
* Which headers should be allowed for CORS requests? This is only used in *
* response to preflight requests. *
* *
***************************************************************************/
headers: 'Authorization' // this line here

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.

Unified Fix for ALL CORS errors in the same app

I know many people have had issues with CORS. I have searched a lot and I have found many answers but I still have errors. The things I tried solved some errors but caused other, all CORS errors...
In my app, I use two API's: an API for the authentication and the mysql requests, the other one is openweathermap. For the latter, I found a solution adding https://cors-anywhere.herokuapp.com/ in front of the API url (http://api.openweathermap.org/ ...). It works well. But I still a errors when I use my own API.
I tried using the same method but my axios call gets a 404.
https://cors-anywhere.herokuapp.com/my_api.test/api/login 404 (Invalid host)
For the login, it works but all other API calls fail. I found that I could add headers in my API to help me:
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Headers: X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method, Authorization");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
Adding this allows other API calls to succeed but the login fails... Is there a way to make all my API calls to be successful ?
Error message:
Access to XMLHttpRequest at 'http://my_api.test/api/login' from origin
'http://localhost:3000' has been blocked by CORS policy: Response to preflight
request doesn't pass access control check: The 'Access-Control-Allow-Origin'
header contains multiple values '*, *', but only one is allowed.
Laravel is both the problem and the solution. There is a file called cors.php in the config folder to deal with this. By default only the path 'api/' is managed. Adding the headers solved the problem for the other paths but caused an error for the path 'api/'.
The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.
By simply replacing api/* by /* in config/cors.php, I can access all paths of my API without having CORS errors.

Browser still send HTTP POST request after preflight request does not allow POST method

I'm confused about that my API server (NodeJS Express) only allow GET method
res.setHeader('Access-Control-Allow-Methods', 'GET');
then I make a CORS POST request with header
headers: {
'Content-Type': 'application/json',
'X-Test-Header': 'TESTING'
}
(the purpose is force browser to call OPTIONS request before a POST request). Then I inspect Network Tab in Chrome Debug, it does send 2 request (first with OPTIONS then POST request). The response header of OPTIONS request is:
Access-Control-Allow-Methods: GET
the question is why it still calls POST request when POST verb is not included in Allow-Methods headers?
Anyone know this please explain to me. Thanks in advance.
P/S: With DELETE or PUT method, it will throw an error
has been blocked by CORS policy: Method PUT is not allowed
by Access-Control-Allow-Methods in preflight response.
I think with POST method it must throw the same error to indicate the method is not allowed
P/S: I'm still facing about this issue, Does anyone know it and explain to me, thanks in advance

CORS Issue: Response for preflight has invalid HTTP status code 403

Environment
Tomcat Server hosting webservice app.
Website is built using SpringMVC and AngularJS.
Core issue:
REST GET triggers OPTIONS request, which fails.
The GET in question returns an application/JSON response.
Initially, I got the error :
"Failed to load resource: the server responded with a status of 403 (Forbidden)
Failed to load http://myServer:8084/wsPRDE/navbar/getnavbartxt: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://myServer:8080' is therefore not allowed access. The response had HTTP status code 403."
Prior to this problem, all we did was have #CrossOrigin in our Controllers. Then httpd.conf was set to add those headers. Which it did for all the script-retrieving from the site (ironically not CORS) but not for the actual CORS responses. When I ensured Access-Control- Allow-Origin, Allow-Methods and Allow-Headers were returned in the response, by coding a CORSFilter into the webservice, the error changed to :
"OPTIONS http://myServer:8084/wsPOFA/navbar/getnavbartxt 403 (Forbidden)
Failed to load http://myServer:8084/wsPOFA/navbar/getnavbartxt: Response for preflight has invalid HTTP status code 403."
Background:
The error was detected in a rather random way.
A process management colleague of mine had a windows 10 update.
Since said update, the entire webservice fails because it can't be reached. Any request from the site itself to the webapplication (same address, different ports), fail. My superior (I'm an intern) then found that in Chrome and IE(when using Server alias, not when using the "normal" URL) have the same issue without the windows 10 update. Prior to the update (or when using IE with normal URL), the GET does not seem to trigger an OPTIONS request.
Further Attempts After this I've tried to expand the Allow-Headers to include all fields present in the GET request/response. I also compare the Origin in the OPTIONS request to a whitelist of Origins, setting the OPTIONS response Allow-Origin header value to the request Origin value if they match.
So the origins match and all the headers in the request/response are set to be allowed. I've spent 3 days so far reading up on CORS and looking for solutions on the web. Most solutions mention the CORS filter I've implemented, and then there are a ton of dirty-workarounds from clearing the header entirely, to always responding 200 OK to OPTIONS requests. But they seem very "dirty" solutions.
Can anyone point me in the right direction? Should I be looking at adding CORS-related code to my Spring Controllers? Is the delegation of constructing httpRequests to AngularJS the crux of my problems? Or am I thinking in the wrong direction entirely?
Currently, my request/response headers look like this:
This question got downvoted, probably because I wasn't exactly clearminded by the time I decided to ask for help. In any case, I managed to resolve the problem today. By the time I posted the question, I had already made a Custom CORS filter at the application level. Changing this filter to manually set the Status Code to 200 for Options if the Allow-Origin and Allow-Methods fields were set and only applying the chain.doFilter() on non-options requests resolved the issue. I'm a bit unsure if it's an ideal/safe solution. But it works. Below you can find the full solution used.
web.xml:
<filter>
<filter-name>SimpleCORSFilter</filter-name>
<filter-class>com.ofis.webservice.CORS.SimpleCORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SimpleCORSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Filter class:
package com.ofis.webservice.CORS;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SimpleCORSFilter implements Filter {
ArrayList<String> AllowedOrigins;
ArrayList<String> AllowedMethods;
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
if (request.getMethod().equals("OPTIONS")) {
String reqOrigin;
reqOrigin = request.getHeader("Origin");
for (String origin : AllowedOrigins) {
if (origin.equals(reqOrigin)) {
response.setHeader("Access-Control-Allow-Origin", reqOrigin);
break;
}
}
String reqMethod;
reqMethod = request.getHeader("Access-Control-Request-Method");
for (String method : AllowedMethods) {
if (method.equals(reqMethod)) {
response.setHeader("Access-Control-Allow-Method", reqMethod);
}
}
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "cache-control,if-modified-since,pragma,Content-Type");
//Checks if Allow-Method and Allow-Origin got set. 200 OK if both are set.
if(!response.getHeader("Access-Control-Allow-Method").equals("") && !response.getHeader("Access-Control-Allow-Origin").equals("")){
response.setStatus(200);
}
} else {
chain.doFilter(req, res);
}

CORS with AngularJS and JAX-RS

I am writing a web application using AngularJS on the front end and JAX-RS on the backend. The front end can make HTTP post requests successfully to the backend when the CORS extension in chrome has 'enable CORS' on. When it is off, it comes back with an error -
The 'Access-Control-Allow-Origin' header contains multiple values 'http://127.0.0.1:XYZXX, *', but only one is allowed. Origin 'http://127.0.0.1:XYZXX' is therefore not allowed access.
The backend has a CORSFilter.java file configured as below -
public class CORSFilter implements ContainerResponseFilter {
#Override
public ContainerResponse filter(ContainerRequest request,
ContainerResponse response) {
response.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
response.getHttpHeaders().add("Access-Control-Allow-Headers",
"origin, content-type, accept, authorization");
response.getHttpHeaders().add("Access-Control-Allow-Credentials","true");
response.getHttpHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
return response;
}
}
I have tried suggestions in various stackoverflow posts like deleting the X-REQUESTED-WITH and setting UseXDomain to true -
delete $http.defaults.headers.common['X-Requested-With'];
$http.defaults.useXDomain = true;
I have also tried changing the content-type to Form/URLEncoded instead of Application/JSON. None seem to enable sending the POSTs except the Chrome extension - which makes me think the solution should be on client side.
Please help! - Thanks in advance.

Resources