I am currently stucked on making post requests to pinterest api. While I feel my code is working well, I keep getting back "Invalid Request Body" from the pinterest api.
Based on the documentation on their site, here is how they said the request should be made:
curl -X POST https://api.pinterest.com/v5/oauth/token --header "Authorization: Basic {base64 encoded string made of client_id:client_secret}" --header 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'grant_type=authorization_code' --data-urlencode 'code={YOUR_CODE}' --data-urlencode 'redirect_uri=http://localhost/'
Here is how I made the request using Axios from my ExpressJS application:
const redirectUrl = process.env.PINTEREST_OAUTH_CALLBACK_URL;
const clientId = process.env.PINTEREST_APP_ID;
const clientSecret = process.env.PINTEREST_APP_SECRET;
let url = 'https://api.pinterest.com/v5/oauth/token';
let accessTokenRequestBody = {
code: encodeURIComponent(code), //code is defined at the top as received from pinterest
grant_type: encodeURIComponent("authorization_code"),
redirect_uri: encodeURIComponent(redirectUrl)
}
console.log(`RequestBody =${JSON.stringify(accessTokenRequestBody, null, 2)}`);
const clientIdAndSecretBase64 = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
axios.post(url, accessTokenRequestBody, {
headers: {
'Content-Type': 'application/application/x-www-form-urlencoded; charset=UTF-8',
'Authorization': `Basic ${clientIdAndSecretBase64}`
}
}).then((response) => {
let responseData = response.data;
console.log(`Pinterest ResponseData = ${JSON.stringify(responseData, null, 2)}`);
}).catch((e) => {
console.log(`${JSON.stringify(e.response.data)}`);
});
Here is the response I keep getting back:
{code:1, message:"Invalid request body"}
Here is the link to the documentation I am working with
https://developers.pinterest.com/docs/api/v5/#tag/Authentication
Please, what could I be doing wrong here, because I believed I provided everything they needed in the request.
Thanks in anticipation for your input.
const redirectUrl = process.env.PINTEREST_OAUTH_CALLBACK_URL;
const clientId = process.env.PINTEREST_APP_ID;
const clientSecret = process.env.PINTEREST_APP_SECRET;
let url = 'https://api.pinterest.com/v5/oauth/token';
let body = {
'code': code, //code is defined at the top as received from pinterest
'grant_type': 'authorization_code',
'redirect_uri': redirectUrl
};
// Fix code
let accessTokenRequestBody = Object.keys(body)
.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(body[k])}`)
.join('&');
console.log(`RequestBody =${JSON.stringify(accessTokenRequestBody, null, 2)}`);
const clientIdAndSecretBase64 = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
axios.post(url, accessTokenRequestBody, {
headers: {
'Content-Type': 'application/application/x-www-form-urlencoded; charset=UTF-8',
'Authorization': `Basic ${clientIdAndSecretBase64}`
}
}).then((response) => {
let responseData = response.data;
console.log(`Pinterest ResponseData = ${JSON.stringify(responseData, null, 2)}`);
}).catch((e) => {
console.log(`${JSON.stringify(e.response.data)}`);
});
Related
I've been trying to send a JSON data using fetch but the backend receives an empty object.
In my Client JS I have
const user = "company1";
const username = "muneeb";
const data = {user, username};
fetch("http://127.0.0.1:3000/users/api/login", {
method: 'POST',
body: JSON.stringify(data)
}).then((response) => {
console.log(response);
});
The server side has:
router.post('/users/api/login', async (req, res, next) => {
try {
// console.log(request.body);
const request = JSON.stringify(req.body);
let imageTitles = [];
console.log(request);
*its random from here on out but you get the idea*
await components.getImages(imageTitles);
const finalKey = imageTitles.join("");
let images = await components.output(req.body.user ,req.body.username);
res.send(components.jsonConverter(imageTitles, images)); //---Top priority
db.setPassword(req.body.user, req.body.username , finalKey);
} catch (err) {
console.log(err);
res.send(err).sendStatus(500);
};
})
A few things I have already tried :
It works perfectly in Insomnia(postman).
express.json() is present , it helped me go from undefined to blank JSON.
I have enabled cors settings.
That's it for now.
The body parser express.json will only be applied for requests with Content-Type: application/json. You have to add the content type to your fetch call:
fetch("http://127.0.0.1:3000/users/api/login", {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
})
I've been trying to get all candidates from Catsone into a Google Sheet and although the code is apparently according to their API instruction, I'm getting the above mentioned error and I'm not sure where to look for the issue.
Here's the code I'm running:
const API_KEY = "XXXXXXXXXXXXXXXXXXXXXXXX";
function getallcandidates() {
const url = 'https://api.catsone.com/v3/candidates';
const params = {
'muteHttpExceptions': true,
'method': 'GET',
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Token' + API_KEY
}
};
const response = UrlFetchApp.fetch(url, params);
const data = response.getContentText();
const json = JSON.parse(data);
Logger.log('Data: ' + json)
}
These are their instructions for authentication: https://docs.catsone.com/api/v3/#authentication
This is what successfully I got whe I tried calling it from Postman:
var myHeaders = new Headers();
myHeaders.append("Authorization", "Token XXXXXXXXXXXXXXXXXXXX");
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
fetch("https://api.catsone.com/v3/candidates", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
Appreciate any help.
When I saw your added Javascript and your Google Apps Script, if the value of const API_KEY = "XXXXXXXXXXXXXXXXXXXXXXXX"; has no space at the top character, how about the following modification?
Modified script:
From:
'Authorization': 'Token' + API_KEY
To:
'Authorization': 'Token ' + API_KEY
From your additional Javascript, 'Token' is modified to 'Token '.
This question already has an answer here:
Error when accessing API with fetch while setting mode to 'no-cors' [duplicate]
(1 answer)
Closed 2 years ago.
I'm trying to send a POST request to a URL, using fetch().
I'm deliberately setting the Authorization header but when I inspect the response in Firefox Dev Tools it outputs following error "Missing request header 'Authorization' for method parameter of type String".
var target_url = "https://api.sonos.com/login/v3/oauth/access";
var encoded_msg = btoa(client_id + ':' + secret); // base64-encodes client_id and secret using semicolon as delimiter
var params = `grant_type=authorization_code` + `&code=${authCode}` + `&redirect_uri=${redirect_uri}`;
var myHeaders = new Headers({
'Authorization': `Basic ${encoded_msg}`,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST',
'Content-Length': params.length,
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
});
fetch(target_url, {
method: 'POST',
mode: 'no-cors',
credentials: 'include',
redirect: 'follow',
headers: myHeaders,
body: params
})
.then(response => {
console.log("Status: " + response.status);
console.log("StatusText: " + response.statusText);
console.log("Type: " + response.type);
console.log("URL: " + response.url);
});
What removes the Authorization-Header, why and how do I prevent it?
Edit:
For Clarification, I'm using Firebase Cloud Functions to host my webpage from which I send the request to the Sonos Authorization API.
Using Postman, the request goes through and I get the correct response.
The step you're performing to retrieve an access token must be performed in a Cloud function endpoint.
Get an access token
Once you’ve gotten an authorization code, use it to get an access token and refresh token. Use the access token to send API calls to the household through the Sonos cloud. This step uses your client secret, so it should be a server-side request rather than a client-side (browser-based) request.
Reference:
https://developer.sonos.com/build/direct-control/authorize/
Bring in node-fetch as a dependency in your package.json because it's API implementation follows closely with browser fetch.
Add an endpoint as follows:
const fetch = require('node-fetch');
const functions = require('firebase-functions');
const secret = functions.config().sonos.secret;
const client_id = functions.config().sonos.client_id;
const redirect_uri = functions.config().sonos.redirect_uri;
exports.retrieveAccessToken = functions.https.onRequest(async (req, res) => {
const {authCode} = req.query;
const target_url = "https://api.sonos.com/login/v3/oauth/access";
const encoded_msg = btoa(`${client_id}:${secret}`); // base64-encodes client_id and secret using semicolon as delimiter
const body = `grant_type=authorization_code&code=${authCode}&redirect_uri=${redirect_uri}`;
const headers = new Headers({
'Authorization': `Basic ${encoded_msg}`,
'Content-Length': body.length,
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
});
const response = await fetch(target_url, {
method: 'POST',
redirect: 'follow',
headers,
body
});
const token_data = await response.json();
return token_data;
});
Modify code in your webpage to make a request to the cloud function endpoint after user returns from Sonos login service.
const authCode = new URLSearchParams(window.location.search).get('code');
fetch(`https://us-central1-<project-id>.cloudfunctions.net/retrieveAccessToken?authCode=${code}`);
I'm trying to make the discord OAuth work. In the doc, it is necessary to generate a code, it works very well this step but after it is to generate the token. It asks to make a POST request with the right parameters but it always brings me the error: {"error":"unsupported_grant_type"}
My code:
app.get('/discord/callback', async function (req, res) {
if (req.query.code === undefined || req.query.code == '') return next();
const response = await fetch("https://discordapp.com/api/v6/auth2/token", {
method: 'POST',
headers: {
"Content-type": "application/x-www-form-urlencoded"
},
data: {
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
code: req.query.code,
redirect_uri: redirect,
grant_type: "authorization_code",
scope: "identify"
}
});
const json = await response.json();
debug('%O', json);
res.send(json);
});
Doc:
def exchange_code(code):
data = {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': REDIRECT_URI,
'scope': 'identify email connections'
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post('%s/oauth2/token' % API_ENDPOINT, data, headers)
r.raise_for_status()
return r.json()
Thanks for your help
Your headers are:
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
which means that it also expects the data as form data and NOT json.
So this should work:
app.get('/discord/callback', async function (req, res) {
if (req.query.code === undefined || req.query.code == '') return next();
const params = new URLSearchParams();
params.append('client_id', process.env.CLIENT_ID);
params.append('client_secret', process.env.CLIENT_SECRET);
params.append('grant_type', 'authorization_code');
params.append('code', code);
params.append('redirect_uri', redirect);
params.append('scope', 'identify');
const response = await fetch("https://discordapp.com/api/v6/auth2/token", {
method: 'POST',
body: params
headers: {
"Content-type": "application/x-www-form-urlencoded"
},
});
const json = await response.json();
debug('%O', json);
res.send(json);
});
You can refer this for better understanding: https://www.npmjs.com/package/node-fetch#post-with-form-parameters
I encountered this issue today as well, and inspired by Aakash Sharma's answer, I build a little utility function(in typescript) that will convert an object to that required format:
export const jsonToUrlParams = (data: Record<string, any>) => {
const params = new URLSearchParams();
for (const key in data) {
params.append(key, `${data[key]}`);
}
return params;
};
I'm trying to write a Post method with Axios in NodeJS.
I have following things to pass as param in post method
url = http:/xyz/oauthToken
header 'authorization: Basic kdbvkjhdkhdskhjkjkv='\
header 'cache-control:no-cache'
header 'content-type: application/x-www-form-urlencoded'
data 'grant_type=password&username=user123&password=password123'
As I tried with following code but new to Axioz not sure how can exactly implement the header with grant type of body response.
var config = {
headers: {'Authorization': "bearer " + token}
};
var bodyParameters = {
data 'grant_type=password&username=user123&password=password123'
}
Axios.post(
'http:/xyz/oauthToken',
bodyParameters,
config
).then((response) => {
console.log(response)
}).catch((error) => {
console.log(error)
});
Any help/suggestion would be appreciated :-)
Currently, axios does not make it convenient to use form-encoded data; it's mostly optimized toward JSON. It's possible, though, as documented here.
const querystring = require('querystring');
const body = querystring.stringify({
grant_type: 'password',
username: 'user123',
password: 'password123'
});
axios.post('http:/xyz/oauthToken', body, {
headers: {
authorization: `bearer ${token}`,
'content-type': 'application/x-www-form-urlencoded'
}
});