Microsoft Graph CORS preflight error on file download - javascript

Context:
I am trying to download files into a buffer for AngularJS application in order to forward the file to the backend for storing on the server (backend is not under my control) therefore I need to get file data into Blob which then gets uploaded my our own servers.
Issue:
Microsoft graph gives me their recommended #microsoft.graph.downloadUrl property in the JSON file which retrieves a list of all files in the currently selected folder. As I use it to download the file itself I get hit with Access to XMLHttpRequest at [the URL from #microsft.graph.downloadUrl] from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
My code:
const req = {
method: "GET",
url: file.item.downloadLink, // #microsoft.graph.downloadUrl
headers: {
Authorization: `Bearer ${this.token}`
},
eventHandlers: getEventUploadEvenHandlers(file), // update download progress bar
responseType: "blob"
};
const ret = await this.$http(req);
My research:
I looked through this article from 5 years ago: https://github.com/microsoftgraph/microsoft-graph-docs/issues/43
I read the entire article about CORS and files here: https://learn.microsoft.com/en-us/onedrive/developer/rest-api/concepts/working-with-cors?view=odsp-graph-online
Of course, I have read the documentation: https://learn.microsoft.com/en-us/graph/api/driveitem-get-content?view=graph-rest-1.0&tabs=http
Nothing has given me an answer to why would the issue persist
If you have any experience with this issue or notice something you think I missed I would be very grateful.
Thank you for any help

So basically my whole approach was wrong.
I deduced from other cloud services that Microsoft Graph would return JSON with data of the file.
MICROSOFT RETURNS THE FILE ITSELF
Therefore fetch or a very simple request without any headers required is enough to get the file and then with .blob() or responseType: 'blob' you get a blob from the response.
(await fetch([#microsoft.graph.downloadUrl])).blob() // returns Blob promise

Related

Uploading a video to the presigned URL from Frontend

I have used my backend code to generate the presigned URL and then used that URL to upload a video that was recorded during the session. I am using the below piece of code in the frontend(React JS) to upload the video the preflight seems to fail with 403 Forbidden and the post request fails with cors error. Please find below the details:
Code used:
static async uploadVideoToS3(url, body) {
try {
const myHeaders = new Headers({ 'Content-Type': 'video/mp4', 'mode': 'no-cors' });
const response = fetch(url, {
method: 'POST',
headers: myHeaders,
body: body
});
return response;
} catch (error) {
toast(error);
console.log(error);
}
}
Console error:
Access to fetch at 'https://xxxxxxxxxx' from origin 'http://localhost:5000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: 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.
Note: Changed the presigned URL to xxxxxxxxxx to avoid leaking the details in the post here.
Could the issue be that I am running all this on localhost? Or Is the CORS Configuration on the AWS S3 Bucket causing this issue? or Is there any header missing in my request?
I found a post that had a similar issue: Getting 403 (Forbidden) when uploading to S3 with a signed URL where the OP responded that the issue was resolved but they never mentioned the resolution.
Try adding proxy to package.json file and add the url of the server you're sending your request to, In your case localhost:5000
"proxy": "http://localhost:5000"
Then restart your app with npm start and change the url in your component, if you are doing it like
const res = fetch('https://localhost:5000/api/user')
then change it and do it like below
const res = fetch('/api/user')
If the above solution doesn't work then there might be a problem with your backend.
I thought so too that as I was trying to hit the URL from local which isn't served on a secure host (HTTP) was causing the issue. So I deployed the app on a dev environment but faced the same issue. After some research, I was able to fix the issue. It seems that I needed to modify the CORS configuration of the S3 Bucket that I was trying to generate the pre-signed URL from.
Before generating the pre-signed URL, I added the below piece of code which changes the CORS configuration of the S3 bucket and since I was accessing it from my local as well I have put the Allowed Origins as * for now but before moving to production, I will change it to the prod URL.
//passing AWS credentials
BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey);
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCreds))
.withRegion(clientRegion)
.build();
//Creating CORS List to update the CORS Configuration
List<String> allowedHeaders=new ArrayList<String>();
allowedHeaders.add("*");
List<AllowedMethods> allowedMethods=new ArrayList<AllowedMethods>();
allowedMethods.add(0, AllowedMethods.GET);
allowedMethods.add(1, AllowedMethods.PUT);
List<String> allowedOrigins=new ArrayList<String>();
allowedOrigins.add("*");
List<String> exposedHeaders=new ArrayList<String>();
exposedHeaders.add("GET");
exposedHeaders.add("PUT");
CORSRule cors = new CORSRule();
cors.setAllowedHeaders(allowedHeaders);
cors.setAllowedMethods(allowedMethods);
cors.setAllowedOrigins(allowedOrigins);
cors.setExposedHeaders(exposedHeaders);
cors.setMaxAgeSeconds(3600);
//Assigning CORS List to CORS Configuration
BucketCrossOriginConfiguration CORSConfiguration = new BucketCrossOriginConfiguration();
CORSConfiguration.withRules(cors);
//Updating CORS Configuration
s3Client.setBucketCrossOriginConfiguration(bucketName, CORSConfiguration);
Alternatively, you can modify the CORS from the frontend/other languages as well, reference:
Javascript: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putBucketCors-property
Other Languages: https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketCors.html#API_PutBucketCors_SeeAlso

Cannot call Apache Airflow REST API using JavaScript Fetch API (CORs Error)

Working with Apache Airflow REST API, and having issues with CORS.
When calling the endpoint using the fetch API in JavaScript I get the following error:
Access to fetch at 'my_url/api/v1/dags/example_bash_operator/tasks' from origin 'my_url' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: 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.
This is how I am calling it:
let url = "my_url/api/v1/dags/example_bash_operator/tasks";
let username = 'my_username';
let password = 'my_password';
let headers = new Headers();
headers.set('Authorization', 'Basic ' + btoa(username + ":" + password));
fetch(url, {
headers: headers,
method: 'GET',
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
I also tried adding mode: 'no-cors' but just get the "unexpected end of input" error.
For some background, the following works fine:
starting the airflow webserver and scheduler
accessing the airflow UI
accessing the SwaggerUI authenticating Swagger and calling the REST endpoints with this tool
calling my_url in the address bar of a new browser tab (returns the expected JSON)
I have set the auth_backend in airflow.cfg:
auth_backend = airflow.api.auth.backend.default
Although with the latest REST API version I don't think this makes a difference since everything is set to deny.
I have also set the access control headers in airflow.cfg as described in the docs:
access_control_allow_headers = origin, content-type, accept
access_control_allow_methods = POST, GET, OPTIONS, DELETE
access_control_allow_origin = my_url
...and also tried with wildcard for the access_control_allow_origin:
access_control_allow_origin = *
So the REST calls work fine through Swagger and through the browser address bar, but I cannot call it with fetch using JS. Note that the JS is in an index.html file on the same server (and same root directory) as the airflow files.
The described behavior makes sense, since CORS is used by the browser to prevent attacks from scripts of different resources.
You are still able to fetch via Swagger, Postman or other tools, even through the browser via address bar. But if the policy does not allow to fetch from a different origin, then the browser prevents fetching from your script, which is probably served on a different port. Origin contains host and port.
Your main issue, I cannot help with at the moment.
I've faced the issue of not being able to set the origin policy within the Airflow 2.0 server/API through the (docker-compose) environment variable AIRFLOW__API__ACCESS_CONTROL_ALLOW_ORIGIN.
Maybe it's related to your issue, since I can see from the url of your question (containing the v1), that you're are also using Airflow 2.x.
By the way, the message from chrome is CORS error: Preflight Missing Allow Origin Header, referring to the question in the comments of the original question.

How can I secure implement third-party API calls using JavaScript in a BigcCommerce store?

I want to make some API requests to the shipping carriers at the BigCommerce product page and I have some credential for that requests which I don't want to show in my JS code. According to the specific environment of BigCommerce I can't make any changes in back end code. I read a lot of similar questions and now have only one question.
Is it only one way to do that using my own API back end web-server which will store credential information and send POST request to the third party API? Then I will receive that information using a POST request via JS to my own API.
I have tried to run Ruby API application on the nginx web-server. However, it was unsuccessful because browser blocked my fetch() request according to the CORS Policy. I tried to add Access-Control-Allow-Origin: * parameter to the server response header writing it in ruby config file but browser didn't recognize it. Also I tried to set up configuration file on the nginx side but that didn't help me with CORS policy response. That's interesting because using Restlet Application I received response from my own API application with correct information and status "ok".
(async function application() {
let dataRuby = {
url: 'http://IP_address/index',
body: {"name": "21312", "year": "2019"}
};
function getApi(data) {
let myInit = {};
myInit.method = "POST";
myInit.body = JSON.stringify(data.body);
myInit.headers = {
'Content-Type': 'application/json'
};
let myRequest = new Request(data.url, myInit);
return fetch(myRequest).then(
res => res.json()
);
}
let response = await getApi(dataRuby);
console.log(response);
})()
Access to fetch at http://IP_address/index from origin 'null' 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.

Cannot 'GET' mLab Data b/c of CORS

I can't execute the 'GET' request with the getTasks() function.
$(document).ready(function(){
getTasks();
});
const apiKey = 'xxxxxxx';
function getTasks(){
$.ajax({
type: 'GET',
url: 'https://api.mlab.com/api/1/databases/taskmanager/collections/tasks?apiKey='+apiKey,
contentType: 'application/json',
xhrFields: {
withCredentials: true
},
success: function(data){
console.log(data);
},
error: function(){
console.log('FAIL')
}
})
}
The error that I get is:
api.mlab.com/api/1/databases/taskmanager/collections/tasks?apiKey=xxxxxxx
Failed to load resource: the server responded with a status of 400
(Bad Request)​
Response to preflight request doesn't pass access control check: 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 400.
I understand that Google-Chrome on Windows is CORS enabled, and will not (by default) allow communication with a different domain. I'm not sure what a preflight request is. Regardless, I tried to implement what I saw from Using CORS - HTML5 Rocks​ (from the CORS from jQuery section), but to no avail.
At a guess, the remote API simply does not respond to pre-flight requests for GET calls (because it shouldn't have to).
Your code is triggering a pre-flight request because it is non-simple. This is due to your adding a Content-type: application/json header. A request Content-type header is used to indicate the request payload format. As it is a GET, there is no payload.
Try this instead...
$.getJSON('https://api.mlab.com/api/1/databases/taskmanager/collections/tasks', {
apiKey: apiKey
}).done(function(data) {
console.log(data)
}).fail(function() {
console.log('FAIL')
})
CORS is there to protect you. If you want some more info on it, wikipedia has a good entry on it.
It appears the issue here is that you're trying to access your mongodb hosted by mlab directly from your web app. As you can see in your code, you're providing credentials/api keys to make that request.
My guess is that mlab's intent of not allowing CORS is to prevent you from doing this. You should never put your private API keys in html to be hosted on a web page, as it's easily accessible by reading source code. Then someone would have direct access to your mongodb.
Instead, you should create a server-side application (node, or... ** Whatever **) that exposes an api you control on the same domain (or a domain you give permission to via CORS).
As far as the "preflight" request, if you look in your chrome debugging tools, you should see an additional request go out with the "OPTIONS" method. This is the request that chrome (and most other http clients) send out first to a server hosted on a different domain. it's looking for the Access-Control-Allow-Origin header to find out whether it's allowed to make the request. Pretty interesting stuff if you ever have some time to dig into it.

Get post response as json

Before I start, I'd like to say sorry for my English, it's not my native language.
I'm trying to setup OAuth2 for GitHub authorization.
I stucked at the step, where I should send POST request to github and receive access token. The problem is that when I send POST request my browser automatically downloads file with access token. Since I can't open this file with javascript, I'm trying to get json as response.
In the documentation it's written that I can change accept header and receive json, but I can't write correct POST request.
I've already tried a lot of things, like this:
$.ajax({
method: "POST",
url: "https://github.com/login/oauth/access_token",
dataType: "application/json"
});
or
$.ajax({
url: 'https://github.com/login/oauth/access_token',
headers: {
Accept : "application/json",
}
data: "data",
success : function(response) {
console.log(response);
} })
etc
But I get this error:
XMLHttpRequest cannot load github.com/login/oauth/access_token. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://braga.fedyunin.com.ua' is therefore not allowed access. The response had HTTP status code 404.
Can't find any useful information in google, so I had to register here. Thanks for help.
Read https://developer.github.com/v3/ in section: Cross Origin Resource Sharing
I tried the same thing, but also failed due to the lack of the Access-Control-Allow-Origin header in the response from GitHub API. I contacted GitHub support and found out what was going wrong.
It doesn't work because you are attempting to use OAuth from a web application, which GitHub API does not support. When you authenticate this way, your client_id and client_secret must be in the web page somewhere and sent with the POST request. The entire request, including your client_secret, can be viewed with Firebug or a similar tool. Because it's a bad idea to expose your client_secret, GitHub API will not return the Access-Control-Allow-Origin header, thus preventing you from retrieving the token.
You must issue the POST from the server side and get the token that way. When you do that, the client_secret is on your server, not in people's browsers.
The Ajax request from your site to github.com fails because browsers follow the same origin policy for xhr requests. This means that an xhr request can only be made for a resource on the same origin.
To allow for cross origin requests, the server needs to Whitlelist domains that can access a particular resource.
In your case, to do this, you need to register your site as an application on your github account, by entering the details here:https://github.com/settings/applications/new

Categories

Resources