Fetch api preflight request failing - javascript

I'm trying to make a GET request from a public API but cant seem to figure out why the cors preflight request keeps failing.
Response for preflight has invalid HTTP status code 404
I've done some searching around and it seems like this is something that needs to be changed server side, but I just want to make sure theres nothing I might be doing wrong. I'm using the following code:
try {
let guideData = await fetch(guideUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${json.access_token}`,
}
});
} catch(err) {
console.log("Failed to fetch data", err);
}
I've tested using Postman and Hurl, both seem to work fine, I just can't get it to work on my client.

When you are sending http request from Postman and Curl the two don't behave like a web browser would. In your case you are using a web browser running js. The code itself looks fine minus the fact that you need to encode your token to make the Authorization work when sent.
The issue definitely seems to be server side, the gist is when you send OPTIONS it will tell you whether or not it's a problem with you or server. Most likely than not from the response you posted, it is definitely CORS.

Related

How can I turn a Node.js http.IncomingMessage into a Fetch Request object?

I'm trying to work with the eBay APIs. It's a small personal project that just needs to run locally, and although I know C#, I'm much more comfortable with Javascript so I'm looking for ways to get this done in JS.
I found this promising looking eBay Node API with browser support. Browser support is something I'm looking for, but it also says that
A Proxy server is required to use the API in the Browser.
They give an example file for a proxy server that is a Cloudflare worker.
I'm trying to translate that into something I can run in Node locally using the basic Node HTTP server. I've been following through it and am doing OK so far, figured out the different ways to access the headers and check them, etc., but now I'm at the point where the proxy server is making the proxy request to the eBay APIs. The way the example file is set up, it seems as though the Cloudflare worker intercepts the HTTP request and by default treats it as a Fetch Request. So then when it goes to pass on the request, it just kind of clones it (but is replacing the headers with "cleaned" headers):
// "recHeaders" is an object that is _most_ of the original
// request headers, with a few cleaned out, and "fetchUrl"
// is the true intended URL to query at eBay
const newReq = new Request(event.request, {
"headers": recHeaders
});
const response = await fetch(encodeURI(fetchUrl), newReq);
The problem is that I don't have a Fetch Request to clone - since I'm running a Node HTTP server, what is event.request in the example code is for me a http.IncomingMessage.
So how can I turn that into a Fetch Request? I'm guessing at the very least there's stuff in the message body that needs to get passed along, if not other properties I'm not even aware of...
I don't mind doing the work, i.e. reading the stream to pull out the body, and then putting that into the Request object somehow (or do I even need to do that? Can I just pipe the stream from the IncomingMessage directly into a Request somehow?), but what else besides the body do I need to make sure I get from the IncomingMessage to put into the Request?
How do I turn a Node http.IncomingMessage into a Fetch Request and be sure to include all relevant parts?
I've made a simple function to convert.
const convertIncomingMessageToRequest = (req: ExpressRequest): Request => {
var headers = new Headers();
for (var key in req.headers) {
if (req.headers[key]) headers.append(key, req.headers[key] as string);
}
let request = new Request(req.url, {
method: req.method,
body: req.method === 'POST' ? req.body : null,
headers,
})
return request
}

How to stop React-Native-Windows from asking for HTTP Basic Auth credentials

I'm using React Native with the plugin for the Universal Windows Platform to access remote resources on a REST server.
When doing a fetch request for a resource that requires authorization via HTTP Basic Auth, I can provide the request with an additional "Authorization" header and everything works fine as long as the credentials are correct.
If the credentials are wrong I'm presented with a Windows-native login prompt (similar to the one when connecting to a remote computer). This prompt is not managed by my app, but automatically seems to pop up when the underlying network connection detects a 401 Unauthorized server response.
Here is what I do inside React Native:
let encodedCredentials = new Buffer(this.state.username + ":" + this.state.password).toString("base64");
let response = await fetch(this.state.serverUrl, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': "Basic " + encodedCredentials,
}
});
let responseJson = await response.text();
alert(responseJson);
The server response, when provided with incorrect credentials, includes:
Status Code: 401 Unauthorized
WWW-Authenticate: Basic realm="iOSResource"
Note that the native login prompt seems to delay the fetch request as a whole. I can enter wrong credentials and confirm the prompt multiple times without the alert firing once. Only when the prompt is explicitly cancelled, the correct credentials are entered or wrong credentials have been tried a couple of times the fetch await continues.
Unfortunately this brings up another issue: When entering wrong credentials in the popup prompt a couple of times and it finally "gives up", I can supply whatever credentials I want to the fetch request, it will not supply my own authorization header to the server in any future requests. The data sent will be stuck to whatever I entered in the prompt before it closed. In this case it does not bring up the prompt again and the request just immediately fails. That leaves me unable to correct the credentials in my own app, because they are simply not sent within the request. I have confirmed this by inspecting the outgoing data in Wireshark.
I guess Windows seems to transparently tamper with the network request to intercept special response codes and re-prompt credentials if necessary before returning the request result to the actual caller for the first time.
I want to deal with incorrect credentials in the app, instead of causing Windows to intercept requests. Is there a way to suppress this native prompt and immediately proceed with my own code in case the Basic Auth fails?
Edit: The behavior is exactly the same when using Axios instead of plain fetch. Seems like both ultimately do a XMLHttpRequest, which is filtered the same way.

Unable to call web-service from angularjs app

Unable to call post webservice from my application. following is the code.
var postLogin = "http://0.0.0.0:000/ddd/v1/login";
var loginvalue = {"email":"some#mail.com","password":"cbsjc6dw3bgjyfdgdKHGGDF="};
var config = {
headers: {
'Content-Type': 'application/json'
}
}
$http.post(postLogin ,loginvalue,config ).success( function(response) {
alert("response "+ response)
$scope.defer.resolve(response);
}).error(function(error){
alert("dddddd" + JSON.stringify(error));
})
If i write this code then it is returning as 400 error but if i use the postman application of google then i am getting the response without any error. So i am in confusion that whatever the code i have written is right or wrong. Hence i need to solve this issue.
Please go through the above image.
This usually happens when Client and Server are on different domains. The POST requests done by the client are first verified with a OPTIONS pre-flight check, to see if a POST would be possible. Sometimes, servers are configured to not allow OPTIONS request method. This will be the outcome of a pre-flight OPTIONS check, in such a case.
There is more information here - Why is an OPTIONS request sent and can I disable it?
Other resources for understanding the concept and helping us to configure the Response headers from the Server-side application are here:
https://medium.com/#praveen.beatle/avoiding-pre-flight-options-calls-on-cors-requests-baba9692c21a
https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
At the end of the day, if the Server is NOT configured to handle Cross-site requests, nothing can be done from the client-side.
Also, there are cases where the server does allow cross-site request, processes and send the response back to client, without the Access-Control-Allow-Origin header or with the Access-Control-Allow-Origin header, but not the same as the request origin or a wildcard "*". In such cases, browser stops processing the response, even when the call turns out to be in HTTP 200 OK Status.
Below is one such example, that I recently encountered while integrating with an external application.

How to return an HTTP response in Javascript?

I have a pop-up banner that looks something like this:
if (confirm("Banner message")) {
//do something if they accept
}
else {
//do something else if they don't accept
}
If the user does not accept, I want to return a 403 Forbidden, preferably in a way that will get picked up by my httpd logs. What is the simplest way to do this?
confirm is an extension to JavaScript provided to JS by browsers that runs, client-side, in a webpage.
You cannot (directly) send an HTTP response in reaction to it, because it is running in an HTTP client.
If you want to log something in your server logs, then you need to make an HTTP request to your server.
This would typically be done using the fetch or XMLHttpRequest APIs. If you want a standard 403 Forbidden error to be displayed to the user, then you can assign a URL to location that your server will return a 403 for instead.
Since you want a 403 Response, you need to request this from the server, since it's an error message sent by the server. I suggest you use window.location = "/someSpecialPath"; and make a 403 error using .htacces or something to that path:
deny from all

CORS error, but data is fetched regardless

I have a generated React site I am hosting in an S3 bucket. One of my components attempts to fetch something when loaded:
require('isomorphic-fetch')
...
componentDidMount() {
fetch(`${url}`)
.then(res => {
console.log(res);
this.setState({
users: res
})
})
.catch(e => {
// do nothing
})
}
The url I am fetching is an AWS API Gateway. I have enabled CORS there, via the dropdown, with no changes to the default configuration.
In my console, for both the remote site and locally during development, I see:
"Failed to load url: No 'Access-Control-Allow-Origin' header is present on the requested resource." etc
However, in the Chrome Network tab, I can see the request and the response, with status 200, etc. In the console, my console.log and this.setState are never called, however.
I understand that CORS is a common pain point, and that many questions have touched on CORS. My question: Why does the response show no error in the Network tab, while simultaneously erroring in the console?
The fetch(`${url}`) call returns a promise that resolves with a Response object, and that Response object provides methods that resolve with text, JSON data, or a Blob.
So to get the data you want, you need to do something like this:
componentDidMount() {
fetch(`${url}`)
.then(res => res.text())
.then(text => {
console.log(text);
this.setState({
users: text
})
.catch(e => {
// do nothing
})
}
Failed to load url: No 'Access-Control-Allow-Origin' header is present on the requested resource." etc
That means the browser isn’t allowing your frontend code to access the response from the server, because the response doesn’t include the Access-Control-Allow-Origin header.
So in order for the above code to work, you’ll need to fix the server configuration on that server so that it sends the necessary Access-Control-Allow-Origin response header.
However, in the Chrome Network tab, I can see the request and the response, with status 200, etc. In the console, my console.log and this.setState are never called, however.
That’s expected in the case where the server doesn’t send the Access-Control-Allow-Origin response header. In that case, the browser still gets the response — and that’s why you can see it in the devtools Network tab — but just because the browser gets the response doesn’t mean it will expose the response to your frontend JavaScript code.
The browser will only let your code access the response if it includes the Access-Control-Allow-Origin response header; if the response doesn’t include that header, then the browser blocks your code from accessing it.
My question: Why does the response show no error in the Network tab, while simultaneously erroring in the console?
For the reason outlined above. The browser itself runs into no error in getting the response. But your code hits an error because it’s trying to access an res object that’s not there; the browser hasn’t created that res object, because the browser isn’t exposing the response to your code.
You may be seeing the status 200 for the OPTIONS not the GET. There is a setting for CORS to handle legacy, so it won't confuse your client. I had to do that last time in a React app. Your error is that your CORS isn't configured properly (sorry, obviously). Chrome won't let your client tlak to the backend if it doesn't get the headers properly. Other browsers probably also, probably React also. It may be some kind of HTTP protocol if only one side has CORS enabled. Someone can correct me there. It's a similar security consideration as sending a request to HTTP from HTTPS. Chrome blocks it.
It looks to me like it's your backend. CORS isn't active or it would put that header on, and after that, you would see errors about origin mismatch in the frontend client.
In my experience, it's a 2-3 step combo, make sure OPTIONS don't send confusing signals to your client (look for settings to do with 200). This is a config setting in your backend. Then, make sure the backend is configured to use CORS. You very specifically need to enter the origin hostname and port that the backend is to expect traffic from.
I could probably give better input if I see what languages and/or frameworks you are using besides React.
This is what you would do in Express JS and node for your Backend:
const cors = require('cors')
// note http or https
app.use(cors({
origin: 'http://example.com:1337',
//origin: '*',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
optionsSuccessStatus: 200
// some legacy browsers (IE11, various SmartTVs) choke on 204
}))
My last React app was detonating without optionsSuccessStatus by throwing success when it was fail.
To give you a little bit of imagery to work with, CORS is simple but finicky. It's a simple matter of alignment. Once your backend is configured to a) use CORS and b) know who to accept traffic from, it's done. Once your frontend is configured to handle this traffic, it's done. It's like aligning a square peg in a round hole until you get the config settings aligned.
Try using Postman to send some GET requests to the Backend. You can observe the headers from there.

Categories

Resources