Execute OAuth2 with the Mailchimp API in NodeJS - node.js

I've been following MailChimp's OAuth2 tutorial (https://developer.mailchimp.com/documentation/mailchimp/guides/how-to-use-oauth2/) but got stuck when making a post request with the code and secret/key params. The closest I've gotten is a 400 Bad Request response. My code:
var args = {
headers: { "Content-Type": "application/json" },
body: "grant_type=authorization_code&client_id=" + client_id + "&client_secret=" + client_secret + "&redirect_uri=" + encoded_url + "&code=" + req.query.code,
url: "https://login.mailchimp.com/oauth2/token"
};
var r = request.post(args, function(err, req, body){
console.log(err)
console.log(req)
})

Okay, I was running in to the same thing, and finally found it. My code was very similar to yours:
this.globals.request({
method: "POST",
uri: "https://login.mailchimp.com/oauth2/token",
form: {
grant_type: "authorization_code",
client_id: this.globals.mailchimp_client_id,
client_secret: this.globals.mailchimp_client_secret,
redirect_uri: encodeURIComponent(fullUrl),
code: req.query.code
}
},
function (error, response, body) {
if (error || response.statusCode !== 200) {
self.globals.logger.debug(self.moduleName, "getMailchimpToken", "Error " + response.statusCode + "(" + response.statusMessage + ") from mailchimp: " + error, true);
body = {};
}
self.globals.logger.debug(self.moduleName, "getMailchimpToken", "got body: " + JSON.stringify(body), false);
deferred.resolve(body);
});
The problem here, and I suspect with yours, is that you are using an encoded URL in the form data, but request will encode it again. When I looked at the querystring, I saw things like
&redirect_uri=http%253A%252F%252F
where the %s were being re-encoded. Changed encodeURIComponent(fullUrl), to just fullUrl, and it worked.
One important note that was not clear to me - the redirect_uri should be the same one used in the OAuth call, including the path. Note that per the Mailchimp docs, you can vary the path in the actual call from the redirect_url used when registering the app, which is where this can be important.

Related

DBS API Server Error 500 on Sandbox calls

I was just experimenting with the DBS API Sandbox here (https://www.dbs.com/developers/). We have to use OAuth2 to get the access token before passing it in the header to authenticate API calls. When I tested everything out on their test client UI I got the results back from the API call.
However, when I coded my simple node app, I could successfully perform OAuth2 and get an access token, but when passed in together with the clientId, got a server error 500 from the API.
For example, the GET call to get a bank account details:
const options = {
method: 'GET',
uri: dbsHost + rootPath + '/accounts/22909292550739201999095',
headers: {
'Content-Type': 'application/json',
'accessToken': accessToken,
'clientId': clientId
}
}
request(options, (error, response, body) => {
// Server error 500
console.log(response)
})
These were the exact instructions as those given on the test client UI. On closer inspection I realised the access token I always got back had 488 characters, but that of the test client UI only had 475. Any reason for this difference? How do I know OAuth2 was performed correctly and I'm getting the right access token?
Here's how I did OAuth2:
On the frontend:
let authorizationUri = dbsHost + rootPath + '/oauth/authorize'
authorizationUri += '?client_id=' + clientId
authorizationUri += '&redirect_uri=' + redirectUri
authorizationUri += '&scope=Read'
authorizationUri += '&response_type=code'
authorizationUri += '&state=0399'
windows.location.assign(authorizationUri)
And at the redirect_uri endpoint once I get back the authorization code:
app.get('/authCallback', (req, res) => {
let authCode = req.query.code
// Encode client_id:client_secret in base-64 to pass in as Auth Basic Header
let b64 = new Buffer(clientId + ':' + clientSecret).toString('base64')
const options = {
method: 'POST',
url: dbsHost + rootPath + '/oauth/tokens',
headers: {
'Authorization': 'Basic ' + b64
},
form: {
'code': authCode,
'redirect_uri': redirectUri,
'grant_type': 'token',
'state': '0399'
}
}
request(options, (error, response, body) => {
if (error) {
console.error('Access Token Error', error.message)
return res.status(500).json('Authentication failed');
}
let json = JSON.parse(body)
let accessToken = json['access_token']
// Make a call with this access token
})
})

Receiving 401 "Bad Credentials" when hitting github API endpoint

I'm trying to build a CLI that uses the GitHub api. I instantly run into a road block. Although I've read the introductory docs up and down, I don't see what is wrong in the following code.
var userData = require('../userData');
var request = require('request');
module.exports = {
hitEndpoint: function() {
var username = "<redacted_user_name>",
password = "<redacted_password>",
auth = "Basic " + new Buffer(username + ":" + password).toString("base64");
var options = {
method: 'get',
url: " https://api.github.com/<redacted_user_name>",
headers: {
"Authorization": auth,
"User-Agent": "<redacted_whatever_doesnt_matter>"
}
}
request(options, function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); // Print the HTML for the Google homepage.
});
},
}
prints:
error: null
statusCode: 404
body: {"message":"Not Found","documentation_url":"https://developer.github.com/v3"}
To get your own profile you'll want to hit the authenticated user endpoint, you shouldn't replace this with your own username, GitHub will know who you are based on your authentication string:
https://api.github.com/user
To get another user's profile you'll want to hit the users endpoint:
https://api.github.com/users/:username

Spotify WebAPI authorization - client credentials flow error invalid_client

Straight forward question, hopefully with a straight forward answer. I'm attempting to implement the client credentials flow via Node.js, using request. Here's my code
var request = require('request');
var payload = config.spotify.clientID + ":" + config.spotify.clientSecret;
var encodedPayload = new Buffer(payload).toString("base64");
var opts = {
url: "https://accounts.spotify.com/api/token",
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Bearer " + encodedPayload
},
body: "grant_type=client_credentials&scope=playlist-modify-public playlist-modify-private"
};
request(opts, function (err, res, body) {
console.log('error', err);
console.log('status', res.statusCode);
console.log('body', body);
});
No matter what I do, the response body is always
{"error":"invalid_client"}
I've tried making the request using curl, with the same outcome.
$ curl -X POST -H 'Authorization: Bearer <base64encoded client_id:client_secret>' -d 'grant_type=client_credentials&scope=playlist-modify-public playlist-modify-private' https://accounts.spotify.com/api/token
That would imply that it's a problem with the credentials. I'm definitely using the right clientID and clientSecret for my app, which leads me to believe that it's the encoding that's causing the problem.
Am I doing the encoding right? If so, what else might be the cause?
Replace
"Authorization": "Bearer " + ...
with
"Authorization": "Basic " + ...
and see if it works better.

Podio API addItem call

I'm trying to implement https://developers.podio.com/doc/items/add-new-item-22362 Podio API addItem call in a nodejs module. Here is the code:
var _makeRequest = function(type, url, params, cb) {
var headers = {};
if(_isAuthenticated) {
headers.Authorization = 'OAuth2 ' + _access_token ;
}
console.log(url,params);
_request({method: type, url: url, json: true, form: params, headers: headers},function (error, response, body) {
if(!error && response.statusCode == 200) {
cb.call(this,body);
} else {
console.log('Error occured while launching a request to Podio: ' + error + '; body: ' + JSON.stringify (body));
}
});
}
exports.addItem = function(app_id, field_values, cb) {
_makeRequest('POST', _baseUrl + "/item/app/" + app_id + '/',{fields: {'title': 'fgdsfgdsf'}},function(response) {
cb.call(this,response);
});
It returns the following error:
{"error_propagate":false,"error_parameters":{},"error_detail":null,"error_description":"No matching operation could be found. No body was given.","error":"not_found"}
Only "title" attribute is required in the app - I checked that in Podio GUI. I also tried to remove trailing slash from the url where I post to, then a similar error occurs, but with the URL not found message in the error description.
I'm going to setup a proxy to catch a raw request, but maybe someone just sees the error in the code?
Any help is appreciated.
Nevermind on this, I found a solution. The thing is that addItem call was my first "real"-API method implementation with JSON parameters in the body. The former calls were authentication and getApp which is GET and doesn't have any parameters.
The problem is that Podio supports POST key-value pairs for authentication, but doesn't support this for all the calls, and I was trying to utilize single _makeRequest() method for all the calls, both auth and real-API ones.
Looks like I need to implement one for auth and one for all API calls.
Anyway, if someone needs a working proof of concept for addItem call on node, here it is (assuming you've got an auth token beforehand)
_request({method: 'POST', url: "https://api.podio.com/item/app/" + app_id + '/', headers: headers, body: JSON.stringify({fields: {'title': 'gdfgdsfgds'}})},function(error, response, body) {
console.log(body);
});
You should set content-type to application/json
send the body as stringfied json.
const getHeaders = async () => {
const headers = {
Accept: 'application/json',
'Content-Type': 'application/json; charset=utf-8',
};
const token = "YOUR APP TOKEN HERE";
headers.Authorization = `Bearer ${token}`;
return headers;
}
const createItem = async (data) => {
const uri = `https://api.podio.com/item/app/${APP_ID}/`;
const payload = {
fields: {
[data.FIELD_ID]: [data.FIELD_VALUE],
},
};
const response = await fetch(uri, {
method: 'POST',
headers: await getHeaders(),
body: JSON.stringify(payload),
});
const newItem = await response.json();
return newItem;
}

invalid oauth2 token request

I'm developing a node application which needs to authenticate with google. When I request a token, https://accounts.google.com/o/oauth2/token responds with:
error: 400
{
"error" : "invalid_request"
}
I've tried making the same request in curl, and have received the same error, so I suspect there is something wrong with my request but I can't figure out what. I've pasted my code below:
var request = require('request');
var token_request='code='+req['query']['code']+
'&client_id={client id}'+
'&client_secret={client secret}'+
'&redirect_uri=http%3A%2F%2Fmassiveboom.com:3000'+
'&grant_type=authorization_code';
request(
{ method: 'POST',
uri:'https://accounts.google.com/o/oauth2/token',
body: token_request
},
function (error, response, body) {
if(response.statusCode == 201){
console.log('document fetched');
console.log(body);
} else {
console.log('error: '+ response.statusCode);
console.log(body);
}
});
I've triple checked to make sure all the data I'm submitting is correct and i'm still getting the same error. What can I do to debug this further?
It turns out that request.js (https://github.com/mikeal/request) doesn't automatically include the content-length to the headers. I added it manually and it worked on the first try. I've pasted the code below:
exports.get_token = function(req,success,fail){
var token;
var request = require('request');
var credentials = require('../config/credentials');
var google_credentials=credentials.fetch('google');
var token_request='code='+req['query']['code']+
'&client_id='+google_credentials['client_id']+
'&client_secret='+google_credentials['client_secret']+
'&redirect_uri=http%3A%2F%2Fmyurl.com:3000%2Fauth'+
'&grant_type=authorization_code';
var request_length = token_request.length;
console.log("requesting: "+token_request);
request(
{ method: 'POST',
headers: {'Content-length': request_length, 'Content-type':'application/x-www-form-urlencoded'},
uri:'https://accounts.google.com/o/oauth2/token',
body: token_request
},
function (error, response, body) {
if(response.statusCode == 200){
console.log('document fetched');
token=body['access_token'];
store_token(body);
if(success){
success(token);
}
}
else {
console.log('error: '+ response.statusCode);
console.log(body)
if(fail){
fail();
}
}
}
);
}
from here How to make an HTTP POST request in node.js? you could use querystring.stringify to escape query string of request parameters. Plus you'd better add 'Content-Type': 'application/x-www-form-urlencoded' for POST request.
post here the final string generated from token_request var.that may have something wrong. or may be authentication code is expired or not added correctly to the URL. Usually code has '/' in it that needs to escaped.

Resources