Bad Request (400) When Trying to Authenticate Harvest API in React - reactjs

So I'm building a status board for our internal use as developers here in the office. It will show number of commits, hours tracked, etc.
I am following this model for authenticating. After a user logs in with harvest it redirects them back to the app with the code param as a query string, I'm then taking that query string and passing it to a state to then do a fetch to get the access token (so that I can later pull API data).
What happens, is the login is successful but when you are redirected back to the app the fetch throws a Bad Request (400) error. I have tested in Postman and can get the correct response so I'm not sure what the issue is...
Here is some of the JS from the Main component that sets the states if there is a code param:
harvestState() {
// grab URL params
let urlParams = queryString.parse(location.search);
console.log(urlParams);
console.log(urlParams.code);
// set the state based on the paramater passed back
urlParams.code ? (
this.setState({
harvestcode: urlParams.code
})
) : (
this.setState({
harvestcode: 'none'
})
);
}
componentWillMount(){
this.harvestState();
}
And here is the fetch function in my Harvest component:
getHarvest(){
const clientSecret = 'XXXXXXXXXX';
// Set Harvest Headers
const harvestHeaders = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
method: 'POST',
mode: 'no-cors',
body: {
'code': this.props.code,
'client_id': this.props.clientid,
'client_secret': clientSecret,
'redirect_uri': 'http://dash.mycompany.me',
'grant_type': 'authorization_code'
}
};
fetch('https://mycompany.harvestapp.com/oauth2/token', harvestHeaders)
.then( response => response.json() )
.then( token => {
console.log(token);
} )
}
componentDidMount(){
if( this.props.code !== 'none' ){
this.getHarvest();
}
}
Is there something here that I am doing wrong? Why does it always return a bad request? Any help would be appreciated. Thank you!

At least one issue you have is that when you use mode: 'no-cors' you’re telling the browser to handle the response as an opaque response, which means that you’re telling the browser to not make any properties of the response object accessible from JavaScript.
So if you make a mode: 'no-cors' request, response => response.json() is going to fail.
The only purpose for no-cors in practice is in combination with Service Workers when you’re just caching resources (e.g., images) from responses, without need to get properties of the responses.
Anyway, given that the client Web app making the requests in your deployment is running from a different origin than the server the requests are sent to, browsers are going to block the requests unless the server responds with the necessary CORS headers—Access-Control-Allow-Origin, for a start. For an explanation, see the MDN article HTTP access control (CORS).
That is, browsers block cross-origin requests made from JavaScript unless the server the requests are sent to opts-in to allowing those, with the Access-Control-Allow-Origin, etc., response headers. The reason Postman doesn’t block such requests is that Postman is not an arbitrary Web app running at some specific origin on the Web but is instead a browser plugin that you’ve intentionally installed. So it’s not bound the cross-origin restrictions browser enforce for Web apps.

Related

CORS issue on Localhost, MULTIPLE workarounds have failed

I am in tears as I type this, sobbing into my keyboard
I have wasted over a week trying to understand what is wrong. I can no longer learn or develop. I am going to have to give up learning to code if I cannot fix this.
I understand what CORS is. If I had the ability to edit the API server, I would, but I do not. I must use a 3rd party API. I do not have the time to develop my own to work with. I am UNABLE to develop using anything but a local server. I need to stop CORS issues.
I have been trying to fix this problem for days. I am at a complete standstill with my learning. A week of tears and frustratiion has ben spent on this.
I CANNOT:
use https://cors-anywhere.herokuapp.com. This fails and CORS still screams:
Access to XMLHttpRequest at 'https://cors-anywhere.herokuapp.com/https://randomuser.me/api/?page=1&results=10' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
Develop non-locally
Proxy. Package.json just ignores it. I have made multiple attempts to use proxying to avoid CORS. All attempts have ended in complete failure.
I legitimately do not know what to do. This is my code. Please help me, I am in tears and I cannot learn React anymore until this is fixed.
import axios from "axios";
let getFriends = () => {
const config = {
method: "GET",
url: "https://cors-anywhere.herokuapp.com/https://randomuser.me/api/?page=1&results=10",
crossdomain: true,
headers: {
"Content-Type": "application/json"
},
};
return axios(config);
};
export { getFriends };
The reason you are going crazy trying to find a solution is because there is nothing YOU can do about CORS. Changing any header as the other solution recommends won't work, the server will still flag any origins which it does not allow calls from!
This is a value controlled by the SERVER whose API you are calling. And, as the error says, the server you are calling does not accept connections from localhost:3000.
As long as you are making requests from a browser that is on localhost:3000 to this API, you'll be hit with CORS issues.
If you want to ensure that the API itself is not broken, use a different tool like Postman to call to the API. This is like a 'server to server' call, and as such does not require CORS checks.
EDIT: The following call to the random user API seems to work:
import React, { useEffect } from 'react';
function App() {
const fetchRandomUsers = async () => {
const response = await fetch("https://randomuser.me/api/?page=1&results=10")
const json = await response.json()
json.results.forEach((result) => {
console.log(result)
})
}
// On mount, fetch random users
useEffect(() => {
fetchRandomUsers()
}, [])
return (
<></>
);
}
export default App;

Why does CORS not make a request?

I have a problem with a CORS request
Access to fetch at 'https://webhook.site/f9087e12-b444-4e6b-9e64-06ba47b8c24e' from origin 'https://localhost:5001' 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.
My request:
fetch("https://webhook.site/f9087e12-b444-4e6b-9e64-06ba47b8c24e", {
method: 'get',
headers: new Headers({
'Access-Control-Allow-Origin': 'https://localhost:5001',
'Content-Type': 'application/json;charset=utf-8'
}),
})
.then(res => res.json())
.then(
(result) => {
console.log('res ' + result);
},
(error) => {
console.log('error ' + error);
}
)
I can’t figure out how to solve the problem.
CORS is a server side thing. Your browser is contacting webhook.site and saying "I want to download that thing f9087e12-b444-4e6b-9e64-06ba47b8c24e, what domains will you serve it to?" and webhook.site is saying e.g. "I'll only serve it to scripts that were downloaded from whatever.com" and your browser is thinking "hmm, well the script making the request was downloaded from localhost so.. denied"
You need to change the webhook.site server so it has an allow for localhost, not the client. It is the server response to the OPTIONS request that must contain the Access-Control-Allowed-Origin header, not the request from the client. Or you need to make the request without CORS in which case the browser will not make the options request.
CORS headers shall be returned from the server.
The server behind https://webhook.site/f9087e12-b444-4e6b-9e64-06ba47b8c24e shall return the header 'Access-Control-Allow-Origin': 'https://localhost:5001' in response to all requests, especially the OPTIONS request (preflight).
Best intro to the subject I have seen is https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
In a nutshell, the server shall decide if it allows CORS in general and to what resources.
The browser is responsible to protect the user by obeying to the rules defined by the server.
Hope it is helpful...
This is a browser's way of protecting it's users. Your request may be innocent, but imagine a scenario where the user goes to an innocent looking website which then uses http to send any password a user inputs to a thief. So the browser will only allow requests to the same domain or "origin".
There are ways around it, but the commonly accepted practice is to send the request to your server (localhost:5001) and your server sends a request to the other site and sends the response back to the browser.
That way all requests go through the domain that the user decided to trust by visiting.

Can't connect to API using React.js fetch method

I'm trying to connect to buffer's API (https://buffer.com/developers/api/oauth) via react.js using fetch(), but I'm getting a 400 error response.
This is running on my localhost but the site's accessible from the Internet.
Here's my code:
const queryString = require('query-string');
const parsed = queryString.parse(window.location.search);
const buffer_data = {
client_id: BUFFER_CLIENT_ID,
client_secret: BUFFER_CLIENT_SECRET,
redirect_uri: BUFFER_CALLBACK_URL,
code: parsed.code,
grant_type: 'authorization_code',
};
fetch(BUFFER_ACCESS_TOKEN_URL, {
method: 'post',
body: JSON.stringify(buffer_data),
}).then( data => {
console.log('data response ' + data);
return data.json();
}).then( response => {
console.log(response);
});
And here's the response:
{"error":"invalid_request","error_description":"Invalid grant_type
parameter or parameter missing"}
And the console prints this:
Failed to load https://api.bufferapp.com/1/oauth2/token.json: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://myserver.com' is therefore not allowed
access. The response had HTTP status code 400. If an opaque response
serves your needs, set the request's mode to 'no-cors' to fetch the
resource with CORS disabled.
I've tried lots of things, like not serializing the data, trying sending many different headers, using a CORS chrome plugin, etc
Before coding this in react, I've successfully connected using PHP, but to do that I had to add an SSL certificate on my server.
I'm also open to use any library but haven't found one to do this job. Or any other fetching method, like axios?
Many thanks!
I don't know which browser you are using, but you might want to add Allow-Control-Allow-Origin
for your chrome browser (if you are using chrome). This is some issue that seems to occur when you are using localhost with chrome.
Also, i highly recommend using axios for fetching API's, it comes with some easy error logs that can help you pinning down the issue pretty fast.
Greetings!

API Request with HTTP Authorization Header inside of componentDidMount

I'm very new to React, and to practice, I am trying to build an application that fetches information from the Yelp API, but I'm having trouble getting a response. Yelp Fusion v3 requires an 'access_token'(which I've successfully received as a response in Postman). So to make this request in my application, I am using Axios. When I am making this request inside of componentDidMount(), as a response I get
XMLHttpRequest cannot load https://api.yelp.com/v3/businesses/search?term=sushi&location=Boston. 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:8080' is therefore not allowed access. The response had HTTP status code 500.
Though it may seem that I am incorrectly specifying the access_token and parameters, when running the same code in a separate file(not part of the application), I get the JSON response that I am looking for in my app.
Here is my componentDidMount():
componentDidMount: function () {
axios.get('https://api.yelp.com/v3/businesses/search?term=Sushi&location=Boston',{
headers: {
Authorization: `Bearer ${token}`
}
})
.then(function(res){
console.log(res)
})
.catch(function(err){
console.log(err)
})
},
I've tried the Yelp node module as well, but I am having no luck. Please help!
This error is a Cross-Origin error.
Web browsers have a catch with AJAX requests: They need to be addressed to the same origin or be authorized by the third-party itself, otherwise they are blocked. Since you have no control over Yelp, I suggest you take a workaround.
Available workarounds
You use something like jsonp. This method basically consists in making the request in a <script> tag. The server will wrap the response inside a Javascript script and it will be loaded unto the page. (https://en.wikipedia.org/wiki/JSONP). The server MUST offer this format for that workaround to work.
You use a reverse proxy. You can set NodeJS to act as one. In this setup, you will make your yelp request to your origin who will redirect it to the yelp server. This works because your Node proxy does not have the same limitations as your browser. (ex: https://github.com/nodejitsu/node-http-proxy)
There may be other ways to get around this, but those are popular methods.
Hope this helps.

redux isomorphic fetch Access-Control-Allow-Origin

I have written a redux application which I am running locally using webpack-dev-server. (port 8080). I am trying to connect to a web service which is running locally at port 9000.
My code to connect to the web service is as follows
return fetch(`http://localhost:9000/movies/${dimensionName.toLowerCase()}list`)
.then(response => response.json())
.then(json =>
dispatch(receivedDimensionAttributesSuccess(dimensionName, json))
)
.catch(error =>
dispatch(receivedDimensionAttributesError(dimensionName, error))
);
This receives an error
Fetch API cannot load http://localhost:9000/movies/yearlist. No 'Access-
Control-Allow-Origin' header is present on the requested resource. Origin
'http://localhost:8080' is therefore not allowed access. If an opaque response
serves your needs, set the request's mode to 'no-cors' to fetch the resource
with CORS disabled.
I googled for the problem and found this thread
Access Control Allow Origin header not present with fetch api call
but I don't like the solution which involved switching to a different library/middleware altogether.
How can I solve the problem with the isomorphic fetch library.
look at me,this works
you must use
let header = new Headers({
'Access-Control-Allow-Origin':'*',
'Content-Type': 'multipart/form-data'
});
instead of
let defaultOptions = {
url:'',
method:'POST',
mode: 'cors',
headers:{
'Access-Control-Allow-Origin':'*'
},
body:null,
};
to enable cors
you can simply add {mode:'no-cors',} to disable the No 'Access-
Control-Allow-Origin' header. For more information you can refer the following url :
https://developers.google.com/web/ilt/pwa/working-with-the-fetch-api
return fetch(`http://localhost:9000/movies/${dimensionName.toLowerCase()}list`**,{mode:'no-cors'**,})
.then(response => response.json())
Access-Control-Allow-Origin is something you can control at client side. It is a security restriction for browsers that requires to be negotiated with the server.
A server can retrieve data from any end-point, but for browser apps this is different. For security reasons you are only allowed to load data via XHR from the same server you have loaded the web page.
If you need to load data from a different server (say an external API) the browser requires that server responds indicating you are allowed.
See description in the "Cross Domain Request" section on http://www.acuriousanimal.com/2011/01/27/working-with-the-javascript-xmlhttprequest-object.html
You can read this https://developer.mozilla.org/zh-CN/docs/Web/API/GlobalFetch/fetch
fetch(url[, init])
set the init param to allow cross-domain request
try

Resources