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

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

Related

best way to tackle: Response to preflight request doesn't pass access control check: It does not have HTTP ok status

ah yes, yet another preflight error. I've been trying to get rid of this for a while now.
So im building a rust-react project and while making a post or even get request for that matter, im facing this same error.
Access to XMLHttpRequest at 'http://127.0.0.1:8000/users/create' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
The host is http://127.0.0.1:8000 and the react origin is http://localhost:3000 . I've enabled CORS in rust, with the requied headers but to no avail. Every request in my application, needs to be accompanied with a global key in the headers to access or post any sort of data. Simply put, any interaction with the server requires this key.
here's my rust code,
pub struct CORS;
#[rocket::async_trait]
impl Fairing for CORS {
fn info(&self) -> Info {
Info {
name: "Add CORS headers to responses",
kind: Kind::Response
}
}
async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) {
response.set_header(Header::new("Access-Control-Allow-Origin", "http://localhost:3000"));
response.set_header(Header::new("Access-Control-Allow-Methods", "POST, GET, PATCH, OPTIONS"));
response.set_header(Header::new("Access-Control-Allow-Headers", "Content-Type, Global-Api-Key"));
response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
response.set_header(Header::new("Access-Control-Max-Age", "86400"));
}
}
And heres the axios instance
const authSend = axios.create({
method: 'post',
baseURL: 'http://127.0.0.1:8000',
data: {},
headers: {
'Global-Api-Key': key,
Accept: 'application/json',
}
})
What i could find by scratching the internet so far is, before any request, your browser send an OPTIONS request to verify the headers before making data available for remote access. So in order to handle this, i simply need to send an HTTP ok response from the server whenever the method is OPTIONS . But wouldnt that defeat the whole purpose of the request in the first place? I know the other solutions on the internet like using a proxy or simply turning off CORS isn't good practice which is why i haven't tried them yet.
I need to know if sending such response is the most reliable way to tackle this error or if theres something more to it.
So turns out that was the only way to make things work. Despite looking for more answers, I sent an 200 OK response and things are good. another issue i was facing was, the axios.create() function used to create custom axios instance wasnt passing the required headers properly whereas when you simply call the axios function like
axios({method: 'post',
url: 'your url',
your data,
headers: { 'Global-Api-Key': key,}
})
It did the job. I dont know how or what the issue is with custom instances and passing headers. this is the page i referred and they stated the same problem with the above solution.

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.

Getting Access-Control-Allow-Headers in preflight error

I have created angular js app in which I have integrate twitch api , the api is
return $http({
url: "https://api.twitch.tv/kraken/streams",
method: "GET",
params: { channel: channel, limit: 1 },
headers: { "Client-Id": "XXXXXXXXXXXXXXX" }
})
the problem is when I reload the page the api is working but when my state changes without page reload I am getting cross origin error from this api.
the error is
Failed to load https://api.twitch.tv/kraken/streams?channel=eliazOne&limit=1: Request header field RefreshToken is not allowed by Access-Control-Allow-Headers in preflight response.
anyone has idea how to resolve cross error
When you make a request to a different domain this is called a cross domain request. Also known as a CORS request.
When you POST / PUT data to a different domain it will make an OPTIONS request first. This is to ensure that the server has Access-Control-Allow-Headers in place on the response. These headers should permit access to the domain you are making the request from. If these headers are not present then when the OPTIONS request is made it will fail and the POST / PUT will never be made.
See here for more info https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests
The simple answer is to just add these headers to your server.
I don't know if this is something Angular does on its own by default or if it's a bug elsewhere in your code, but you or Angular is sending a RefreshToken header as part of your failing request, which is not allowed per Access-Control-Allow-Headers response header in the pre-flight OPTIONS request.
$ curl -XOPTIONS https://api.twitch.tv/kraken/streams -is | grep Headers
Access-Control-Allow-Headers: Accept, Accept-Language, Authorization, Client-Id, Twitch-Api-Token, X-Forwarded-Proto, X-Requested-With, X-Csrf-Token, Content-Type, X-Device-Id
This is something all cross-origin requests do. The browser sends an OPTIONS request to make sure the real request it's about to make is allowed by the criteria set by the cross-origin server.
You need to add a ?callback=? with the URL you are passing along with a callback function. Please follow the link How to use JQuery to Access Twitch streams
This is JQuery example but the concept is same.
$.getJSON('https://api.twitch.tv/kraken/streams/' + name + '?callback=?',
function(channel){
if (channel["stream"] == null) {
$("#all").append("<p>" + channel._links.self + "</p>");
}
else {
$("#all").append("<p>Fail</p>");
}
});
});

CROS OPTIONS request when trying to upload a file with $http angularjs

I'm actually trying to implement a file upload system between my client side angularjs app and my server but i'm having difficulties to implement this feature.
The problem seems to come from the preflight OPTIONS request sent from Chrome.
When I test my route with postman everything work just fine.
Here is a few screen shots of the postman request execution:
First part of postman example
Second part of postman example
As you can see the route has two parameters a library id and a file to be uploaded and an authentification token.
The problems appear when I try to implement an upload feature in my angular web app.Indeed when I call my $http post request a OPTIONS preflight request is sent to my server.This OPTIONS request doesn't seem to have any of the parameters given to the post request it precedes making my authentification middleware (that has the function of validating the user/token) on my server side respond with a 401 error.
More exactly:
XMLHttpRequest cannot load ..... Response for preflight has invalid http status code 401
It seems that those preflight request are made by the browser when say detect a cross origin resource sharing. This is were I hit a brick wall. I cannot seem to understand how to:
- either send the token with the options request to validate this request
- or to bypass this options request to directly send my post request.
my upload function looks like this:
$http({
method: 'POST',
url: 'my-upload-url',
headers: {
'Content-Type': 'multipart/form-data'
},
data: {
library: my-library-id,
file: my-file-to-upload,
token: user-authentification-token
},
transformRequest: function (data, headersGetter) {
var formData = new FormData();
angular.forEach(data, function (value, key) {
formData.append(key, value);
});
var headers = headersGetter();
delete headers['Content-Type'];
return formData;
}
})
.success(function (data) {
})
.error(function (data, status) {
});
My questions are:
Is there a way to actually send my user token in the OPTIONS request to make it valid server side?
Is there a way of formatting my post request (header/data/params) to make it bypass this preflight browser request?
Is the formatting of my post request wrong in any way making it trigger the OPTIONS request from Chrome?
Thank you in advance for your help.
Martin
In cors, the OPTIONS method is used to tell the server what will your request do. The server must handle the OPTIONS correctly so your main request will send normally. The browser will send the OPTIONS request automatically when your request is a complex cross origin request.
To bypass the OPTIONS request your request should be POST and GET and content-type must be application/x-www-form-urlencoded, multipart/form-data, or text/plain and the headers only contain Accept, Accept-Language and Content-Language.
Your request is not wrong. The reason is that your request is a cross origin request and it isn't a simple request.
So the best way to solve this problem is to make your server handle the cors request correctly.
For express you can use https://github.com/expressjs/cors
see more: cors-mdn cors-w3c

Access-Control-Allow-Origin error but request goes through

I'm currently deploying a basic API to my live server and I'm running into (what I think is) a CORS problem but there is some behavior going on that I can't explain.
I'm communicating from an AngularJS front-end to a Laravel 5 (+ laravel-cors) back-end.
I started testing with a simple jQuery AJAX call (below) and when I make a request from my local Vagrant environment (http://dev.example.local/test.html) to http://api.example.com/v1/matches I get an error about Access-Control-Allow-Origin. The weird thing is that the request does come through because the information is stored in the database via the Laravel-based API correctly.
$.ajax({
method: 'POST',
url: 'http://api.example.com/v1/players',
data: {
"username": "username",
"first_name": "First",
"last_name": "Last",
"nickname": ""
}
}).always(function(r) {
console.log(r);
});
Error:
XMLHttpRequest cannot load http://api.example.com/v1/players. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://other.example.com' is therefore not allowed access.
The console.log(r) returns {readyState: 0, responseJSON: undefined, status: 0, statusText: "error"}
I developed the application locally using a Homestead VM (API) and a Vagrant environment (application) and it's working correctly within these environments...
Some observations:
Each of these requests shows up with Method: POST, Status: 200 OK, Type: xhr in my Chrome Developer Tools.
Tools like Postman and PhpStorm's RESTful service tester correctly execute the request and the data is added without errors.
Any ideas on how to further debug this problem are welcome... I've been trying to wrap my head around this for the entire day now and I just don't know what's causing it.
Your server must return an appropriate Access-Control-Allow-Origin header in the response. For example, if the request is being sent from http://stackoverflow.com, then your server must return this header: Access-Control-Allow-Origin: http://stackoverflow.com. You can determine, server-side, what the origin is by looking at the Origin header on the request. If your server does not return this header in the response, you will not have any access to the properties of the response browser-side (such as the status code, headers, or message body). The Same Origin Policy is at the center of this restriction.
The reason you are not seeing any similar issues when the request is sent by Postman or PhpStorm's RESTful service tester is due to the fact that these services do not send an Origin header with the request, as they are not subject to the Same Origin policy. By default, the browser will append this header to any cross-origin ajax requests, as browsers are subject to the Same Origin Policy. In my previous scenario, the request header would look like this: Origin: http://stackoverflow.com. Browsers that implement the CORS spec are required to add this request header so the server is able to determine if the origin of the request has been whitelisted for cross-origin ajax requests. If this is the case, the server will return the proper Access-Control-Allow-Origin header. If not, it can simply omit the header. Browsers that do not implement the CORS spec will simply refuse to send such an ajax request.
Regarding your bewilderment as to why the request is being sent in the first place, that comes down to a distinction between "simple" and "non-simple" CORS requests. For simple CORS requests, the request will always be sent to the server, but the client/JS will not be able to parse the response without proper acknowledgement from the server. Some CORS requests are not simple, so to speak. These are, for example, DELETE or PATCH requests, or POST/GET requests that contain non-standard headers (such as X-headers or a Content-Type of "application/json" as opposed to "multipart/form-data"). In other words, a request is not simple if it cannot be sent without JavaScript. For example, a <form> submit, or a GET request from a <script src="..."> will always send "simple" requests. For non-simple requests, the browser must "preflight" the request. This means that the browser sends an intermediate request, called a preflight, before the original request. This preflight request is an OPTIONS request. The server must than return headers in the response to this preflight that acknowledge any non-standard properties of the original request. If it does, then the browser will send the original request.
You can read more about preflighting and CORS in general on MDN.

Resources