Unable to load Angular templates cross-domain with custom headers in requests - javascript

we have a project for a client that consists of 3 different websites, and we wanted to re-use as much code as possible. We have one project that contains the bulk of the core Angular JavaScript / templates for directives etc.
We are using ASP.NET 4 and Angular 1.5.5.
I am able to render out a template that's held in Project 1 from Project 2 using the absolute URL with CORS enabled.
However, we are also using a request interceptor for authentication for Single-Sign-On between applications, using the request headers. This stops the cross-domain templates from being pulled over into Project 2 with the following error:
XMLHttpRequest cannot load https://localhost:44302/path/to/template.html. Response for preflight has invalid HTTP status code 405.
If I remove the code that is setting an 'Authorization' header, the template works perfectly cross-domain.
It looks like the only difference in the request with the interceptor is it has the following headers:
access-control-request-headers:authorization
access-control-request-method:GET
Could this be causing the template to not be loaded?

This was solved by setting up CORS in the ASP.NET web side instead of the Angular side.
We enabled it in the Startup.cs file with a private method. We have a Web.config setting called Cors.AllowedOrigins which contains a ; separated string with each application we want to allow in CORS.
private void EnableCors(IAppBuilder app)
{
var policy = new CorsPolicy
{
AllowAnyMethod = true,
AllowAnyHeader = true
};
var origins = ConfigurationManager.AppSettings["Cors.AllowedOrigins"];
if (origins != null)
{
foreach (var origin in origins.Split(';'))
{
policy.Origins.Add(origin);
}
}
else
{
policy.AllowAnyOrigin = true;
}
var corsOptions = new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context => Task.FromResult(policy)
}
};
app.UseCors(corsOptions);
}
And we called this in the Configuration method, passing the IAppBuilder.

Adding the header makes the browser make a preflight OPTIONS request, and the server returns a 405 for that OPTIONS request. The browser then aborts the "real" request. Only simple requests can be made without the need of this preflight request. You can only use a limited set of headers to qualify as simple request
If you can serve the template on the same domain and port, it will not be a CORS request and this preflight request will not be made. If not, you can send the correct response to the OPTIONS request using this guide.

Related

Access to XMLHttpRequest at '' from origin '' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present

I went through this article link . Its related to node js . I am currently using Spring boot to create restful web service , react js to build front end .
In the solution it is mentioned that to add the following HTTP header to the response:
Access-Control-Allow-Headers: Content-Type
I am also providing my front end code , can anyone please suggest me how to add this header to my response ? How to solve this problem ?
TodoDataService.retrieveAllTodo(username)
.then(
response=>{
console.log(response)
this.setState({todos:response.data})
}
)
class TodoDataService{
retrieveAllTodo(name) {
return axios.get(`${API_URL}/users/${name}/todos`);
}
}
CORS policy is a security configuration provided by your browser, which means it can be handled different on any other browser. Basically what it means is that your browser is expecting an instruction coming from your Website about how to handle requests for resources that live outside of the server/domain that hosts your Website.
To set this header in your backend using HttpServletResponse:
#GetMapping("/http-servlet-response")
public String usingHttpServletResponse(HttpServletResponse response) {
response.addHeader("Access-Control-Allow-Origin", "https://yourDomainHere.com");
return "This is the response with the new header";
}
Another option using ResponseEntity:
#GetMapping("/response-entity-builder-with-http-headers")
public ResponseEntity<String> usingResponseEntityBuilderAndHttpHeaders() {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Access-Control-Allow-Origin", "https://yourDomainHere.com");
return ResponseEntity.ok()
.headers(responseHeaders)
.body("Response with header using ResponseEntity");
}
You need to change the "yourDomainHere.com" to the actual domain that your XMLHttpRequest is failing to load from.
Here is another approach.
Modern browsers that implement CORS policies, will try to investigate if the AJAX call is allowed or not by placing an additional HTTP request to the domain of the AJAX request you are trying to access. This preflight request will be sent using the OPTIONS verb as follows:
OPTIONS /
Host: yourDomainHere.com 'you are going to'
Origin: http://www.yourWebsiteOriginalDomain.com 'you are going from'
Access-Control-Request-Method: PUT 'Any verb your XMLHttpRequest intends to use'
This allows you to configure a mapping in your backend to handle an OPTIONS call and respond with something similar using the following custom HTTP headers:
Access-Control-Allow-Origin: http://www.yourWebsiteOriginalDomain.com 'you are going from'
Access-Control-Allow-Methods: PUT, DELETE 'verbs that you allow'
It's an error probably originating in your browser. If you are using Chrome, have to tried to use an extension to unblock CORS, like "Cors unblock" or anything similar?

Making Get request to Yammer API works using Postman tool but not with Vue-Resource

I am trying to integrate Yammer API in my Vue.JS project, for Http calls I am using Vue-Resource plugin. While making GET Http call to get posts from Yammer it gives me following error -
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
I tried postman tool and that gives successful response, but when I try to run the same thing in my Vue.JS project using Vue-Resource plugin it wont work.
The Vue.JS code snippet -
function(){
this.$http.get("https://www.yammer.com/api/v1/messages/my_feed.json").then((data)=>{
console.log(data);
});
In main.vue file i have -
Vue.http.interceptors.push((request, next) => {
request.headers.set('Authorization', 'Bearer my_yammer_token')
request.headers.set('Accept', '*/*')
next()
})
Then I tried the code snippets provided by Postman tool for jquery, that too not working.
jQuery code -
var settings = {
"url": "https://www.yammer.com/api/v1/messages/my_feed.json",
"method": "GET",
"timeout": 0,
"headers": {
"Authorization": "Bearer my_yammer_token",
"Cookie": "yamtrak_id=some_token; _session=some_token"
},
};
$.ajax(settings).done(function (response) {
console.log(response);
});
Though, I found similar questions but nothing worked for me.
I am working this to resolve from last 2 days but getting failed again and again. Please guide/help me.
A browser has higher security requirements than a request in PostMan. In a browser, you are only allowed to make XHR requests to your own current host (combination of domain + port) but not to other remote hosts. To nevertheless make a request to a remote host, you can use the browser built-in CORS. By using this, your browser makes a pre-flight request to the remote host to ask if the current page is allowed to request from that host. This is done via the Access-Control response headers. In your case, this header is probably missing or not allowing your page to access, which is why the request does not go through. Please read further into that topic.
However, in your case, using CORS probably won't be a solution for two reasons: To use CORS, the remote host must present a header which allows every requesting host (*) or your specific one. If you cannot set that setting anywhere on the remote host, it won't work. Second, it is not safe to place your authorization token into client-side JavaScript code. Everybody can just read your JS code and extract the authorization token. For that reason, you usually make the actual API call from the server-side and then pass the data to the client. You can use your own authentication/authorization against your server and then use the static authorization key on the server to request the data from the remote host. In that case, you'll never expose the authorization key to your user. Also, on the server-side, you do not have to deal with CORS as it works just like PostMan or curl as opposed to a browser.

CORS policy error while calling remote URL in Angular

Try to call remote API Url but, getting Access-Control-Allow-Origin error. I tried many things like following but, nothing works.
proxy.conf.js
const PROXY_CONFIG = [
{
context: [
"/api/planets"
],
target: "https://swapi.co",
secure: false,
changeOrigin: true,
logLevel: "debug",
bypass: function (req, res, proxyOptions) {
req.headers["Access-Control-Allow-Origin"] = "*";
req.headers["X-Forwarded-Host"] = "localhost:8090";
req.headers["X-Forwarded-For"] = "localhost";
req.headers["X-Forwarded-Port"] = "8090";
req.headers["X-Forwarded-Proto"] = "http";
}
}
];
module.exports = PROXY_CONFIG;
Running with ng serve --port 8090 --proxy-config proxy.conf.js
Can't make any changes in server side because I am using third party API.
Try adding plugin like https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf?hl=en in your chrome browser.
Since you cant change the server side config, so this plugin can do the trick. The browser by default blocks CORS
Since You cannot make any changes on remote server. So it can be escaped using Reverse proxy server. I also faced the same issue while calling linkedin services.
a. There is an https://cors-anywhere.herokuapp.com/ you can append this before your url
and it will temporarily resolve CORS issues.
Since in enterprise scenario you can not use herokuapp.com before your application specific names so better to set below proxy server.
b. Second approach is using rever-proxy approach and set up your own server (local or remote ) for reverse proxying.
https://stackoverflow.com/q/29670703/7562674
You can also implement reverse-proxy like implementation using Spring and Jersey.
https://github.com/hhimanshusharma70/cors-escape
As the error says, a header named Access-Control-Allow-Origin must be present in a CORS response.
Since swapi.co responses include the Access-Control-Allow-Origin header for correct CORS requests (can be tested with a simple fetch('https://swapi.co/api/planets/') from your browser's dev console), the issue may be because of your proxy settings.
Try modifying the response in the proxy bypass method:
bypass: function (req, res, proxyOptions) {
...
// Note that this is "res", not "req".
res.headers["Access-Control-Allow-Origin"] = "*";
...
}
You can't! End of story. If the owner of the api has decided not to allow cross origin requests then you can't. If your are not going to host your app on the https://swapi.co domain then you will not be able to use the api directly from Angular and you will need some kind of pass through api call on the server from .NET, Node, PHP, Java etc.

No 'Access-Control-Allow-Origin' header is present on the requested resource - Angular 5

I'm trying to access web service from my angular service with cross-origin related headers set. But still, I'm not able to access the web service. The browser keeps saying,
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. The response had HTTP status code 403.
I'm able to access the same URL in the browser (chrome) and postman but not in angular application.
private headers = new HttpHeaders()
.set('Access-Control-Allow-Origin', '*')
.set('Content-Type', 'application/json;charset=UTF-8')
.set('Access-Control-Allow-Headers', '*')
.set('Access-Control-Allow-Methods', 'GET, OPTIONS');
public getData(): Promise<Object> {
return this._http.get(this._url, {headers: this.headers})
.toPromise()
.then(response => {
return response;
}
)
.catch(testService.handleError);
}
Is there anything I'm missing here...
There are multiple ways to solve this issue, but first you will need to identify the extra request header parameter that is getting send by the client, if any.
It's earlier to spot this by using any of the broswer's developer console. Another pointer is to check the response/error for options call if any.
Once identified, you will need to enable the appropriate response parameters from the server, in your case it seems the options call is not setting Access-Control-Allow-Origin in the response.
Let me know if this helped you diagnose your issue.
Try JSONP. Basically as per WC3Schools states
JSONP is a method for sending JSON data without worrying about cross-domain issues.
JSONP does not use the XMLHttpRequest object.
Here's an explanation of how JSONP works
So, there is two approaches
If you have an access to edit the web service.
You need to allow Cross Origin Resource sharing.
if u are using Node.js here is an example of code to add
// ~ Cross origin resource sharing ~ //
var cors = require('cors');
// ~ Initialize the app ~ //
var app = express();
// ~ Enbling CORS ~ //
app.use(cors());
You can add a browser extension that enables you fix CORS errors for the angular app while running on browser.
Here is for chrome https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en
Here is for Mozilla https://addons.mozilla.org/en-US/firefox/addon/cors-everywhere/
You can read more at https://enable-cors.org/
I hope that helps. If there is any error let me know through comments.

Angular.js html5 file upload to grails controller

I would like to have the files that are dropped in this angularjs html5 upload example at JSFiddle:
http://jsfiddle.net/danielzen/utp7j/
uploaded to a backend by a grails controller.
While trying to accomplish this I created a simple grails controller:
class UploadController {
def index() {
if (request instanceof MultipartHttpServletRequest){
for(filename in request.getFileNames()){
MultipartFile file = request.getFile(filename)
String newFileName = UUID.randomUUID().toString() + file.originalFilename.substring(file.originalFilename.lastIndexOf("."))
file.transferTo(new File("/home/myuser/temp/$newFileName"))
}
}
render "ok"
}
}
Then I open an ajax XmlHttpRequest2 POST to the grails controller:
xhr.open("POST", "http://localhost:8080/app/upload")
but the grails controller cannot cast the request to MultipartHttpServletRequest,
probably because this ajax way of invoking the grails controller is not using the multipart/form-data way of uploading.
I tried setting the header on xhr for enctype multipart/form-data to no avail.
I'm completely stuck at the moment and would like to know how I can process the uploaded files in the grails controller
I traced the POST request that was generated by the XmlHttpRequest.send(formData) call with the built-in Chrome Developer Tools.
To my surprise the request method was not of type POST with enctype=multipart/form-data but of type OPTIONS.
This hint got me on the right track and with the help of Google I found out that this OPTIONS request happens as a preflight check as per-spec defined by CORS.
From wikipdia:
Cross-origin resource sharing (CORS) is a mechanism that allows
JavaScript on a web page to make XMLHttpRequests to another domain,
not the domain the JavaScript originated from.[1] Such "cross-domain"
requests would otherwise be forbidden by web browsers, per the same
origin security policy. CORS defines a way in which the browser and
the server can interact to determine whether or not to allow the
cross-origin request.[2] It is more powerful than only allowing
same-origin requests, but it is more secure than simply allowing all
such cross-origin requests.
The CORS standard works by adding new HTTP headers that allow servers
to serve resources to permitted origin domains. Browsers support these
headers and enforce the restrictions they establish. Additionally, for
HTTP request methods that can cause side-effects on user data (in
particular, for HTTP methods other than GET, or for POST usage with
certain MIME types), the specification mandates that browsers
“preflight” the request, soliciting supported methods from the server
with an HTTP OPTIONS request header, and then, upon “approval” from
the server, sending the actual request with the actual HTTP request
method. Servers can also notify clients whether “credentials”
(including Cookies and HTTP Authentication data) should be sent with
requests.
Because my grails (tomcat) server was running from localhost:8080 and my html/javascript was running from within the WebStorm IDE on its built-in http server on localhost:63342, the XmlHttpRequest was effectively CORS, because different port numbers on the same host are also considered cross-origin.
Thus, I needed to make sure that the Grails (tomcat) server allowed this, and I was able to do this with the excellent cors plugin for Grails which can be found at https://github.com/davidtinker/grails-cors
After that the request was recognized as a MultipartHttpServletRequest and the file could be fetched from params
Taking a peek at the network when doing the upload from the fiddle you get:
------WebKitFormBoundarygZi30fqQLB2A9qAC
Content-Disposition: form-data; name="uploadedFile"; filename="file.txt"
Content-Type: text/javascript
------WebKitFormBoundarygZi30fqQLB2A9qAC--
I believe then you'll get an "uploadedFile" as the param key into Grails, so you could write a controller action like:
def uploadFile(CommonsMultipartFile uploadedFile) {
///accessing the file data: uploadedFile.bytes, uploadedFile.contentType, uploadedFile.originalFilename
}
To be sure what is being passed do a debug on the params map that the action receives and change the action method parameter name accordingly.
<body ng-controller="FileUploadCtrl">
$scope.selectedFile=[];
$scope.onFileSelect = function ($files) {
$scope.uploadProgress = 0;
$scope.selectedFile = $files;
};
$scope.myData = {};
$scope.upload = function(){
var formData = new FormData();
formData.append("file", $scope.selectedFile[0]);
formData.append("data", myData.message.value);
$http.post('api/upload', formData, {
transformRequest: angular.identity,
headers: {
'enctype': 'multipart/form-data',
'Content-Type': undefined
}
})
.success(function(){
alert("done");
})
.error(function(){
alert("failed");
});
</body>
<form ng-submit="upload()" method="post" enctype="multipart/form-data" name="myData"> <div>
<textarea rows="3" name="message" placeholder="Send Message"></textarea>
<input type="file" ng-file-select="onFileSelect($files)" />
</div>
<button type="submit" class="btn btn-primary">upload</button>
</form>

Categories