CoffeeScript: HTTPS Post to API, handle response - javascript

I'm new to CoffeeScript/JavaScript, but writing a script for Hubot to talk to my Ansible Tower API in Coffee. Below is my code so far:
module.exports = (robot) ->
robot.respond /deploy zabbix agent (.*)/i, (res) ->
host = res.match[1]
https = require 'https'
authbody = JSON.stringify({
username: "awx.api",
password: "example"
})
authrequest = new https.ClientRequest({
hostname: "awx.example.co.uk",
port: 443,
path: "/api/v2/authtoken/",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(body)
}
})
authrequest.end(authbody)
body = JSON.stringify({
limit: "#{host}"
})
request = new https.ClientRequest({
hostname: "awx.example.co.uk",
port: 443,
path: "/api/v2/job_templates/35/launch/",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(body)
}
})
request.end(body)
res.reply "Deploying zabbix agent to #{host}"
In the authrequest section, I post my username and password to the API and it should return in the response JSON in the following format:
{
"token": "8f17825cf08a7efea124f2638f3896f6637f8745",
"expires": "2013-09-05T21:46:35.729Z"
}
My questions is how I store the token to use as my authentication in the later requests.

You can store it in localStorage like so:
localStorage.setItem("token", "8f17825cf08a7efea124f2638f3896f6637f8745",);
and the get it when you make a request and insert in your header
const token = JSON.parse(localStorage.getItem('token'));
request = new https.ClientRequest({
hostname: "awx.example.co.uk",
port: 443,
path: "/api/v2/job_templates/35/launch/",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(body),
'Authorization': 'Bearer ' + token
}
})
request.on('response', function (response) {
response.on('data', function (chunk) {
let json = JSON.parse(chunk);
localStorage.setItem("token", json['token']);
});
});

Related

Axios is not working to get access token?

i have axios call to get the bearer token it is oauth2.0 but for some reason it is failing same call is working in postman.
index.js
export async function getESLAccessToken(apiConfig: any) {
const _body = {
grant_type: apiConfig.authOptions.body.grant_type,
client_id: apiConfig.authOptions.credentials.sdk_voyage.clientId,
client_secret: apiConfig.authOptions.credentials.sdk_voyage.clientSecret,
scope: apiConfig.authOptions.body.scope
};
const _headers = {
"Content-Type": "application/x-www-form-urlencoded",
"appName": "Blink",
"Accept": "*/*",
"Content-Length": 163
};
const request = {
method: 'POST',
_body,
headers: _headers
};
try {
const resp = await axios.post('https://auth/oauth2/token',
request);
console.log(resp.data);
} catch (err) {
// Handle Error Here
throw err;
}
}
This is how request building for axios
{
"method": "POST",
"_body": {
"grant_type": "client_credentials",
"client_id": "abc",
"client_secret": "xyz",
"scope": "APPPII APPPHI"
},
"headers": {
"Content-Type": "application/x-www-form-urlencoded",
"appName": "Blink",
"Accept": "*/*",
"Content-Length": 163
}
}
Error
Error: Request failed with status code 400
Postman working request and response
POST https://auth/oauth2/token
200
163 ms
POST /auth/oauth2/token HTTP/1.1
Headers
User-Agent: PostmanRuntime/7.26.8
Accept: */*
Cache-Control: no-cache
Postman-Token: d90ccc33-1d77-4502-9a41-74080dd3d7a5
Host: qaapih8.corp.cvscaremark.com
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 163
Request Body
grant_type=client_credentials&client_id=abc&client_secret=xyz&scope=APPPII%20APPPHI
HTTP/1.1 200 OK
Response Body
{ "token_type":"Bearer", "access_token":"token returned", "expires_in":3600, "consented_on":164684, "scope":"APPPII APPPHI" }
The second argument of the axios.post function should be the request body. And the third argument should be the Axios request config object (Which you can set your headers). Axios POST Requests
In your case, the code should be like:
const body = {
grant_type: apiConfig.authOptions.body.grant_type,
client_id: apiConfig.authOptions.credentials.sdk_voyage.clientId,
client_secret: apiConfig.authOptions.credentials.sdk_voyage.clientSecret,
scope: apiConfig.authOptions.body.scope
};
const myHeaders = {
// add your headers here
}
const response = await axios.post(
'https://auth/oauth2/token',
body,
{ headers: myHeaders }
);
Replace
const request = {
method: 'POST',
_body,
headers: _headers
};
with:
const request = {
method: 'POST',
data: _body,
headers: _headers
};
As pointed out by the docs in the comments session: https://axios-http.com/docs/req_config

Using node-fetch effectively, getting invalid json response body error *only* the first time I query the server

I have been attempting to collect an API token from an undocumented API of a Ubiquiti EdgeMax Network switch and use it to check the status of the ports then eventually make some changes to the status of them when I receive the correct information.
I have put together some code using NodeJS and node-fetch that allows me to grab the API authentication token then use it to get some information on the interfaces.
It all seems to be working fine but I have noticed that the first time I run the NodeJS application and query it it would throw an
UnhandledPromiseRejectionWarning: FetchError: invalid json response body at... reason: Unexpected end of JSON input
This happens when I request the data to the switch with the API using the token. If I try the second time it works fine, even on multiple consecutive times.
I tried getting tweaking the async functions but the behavior is the same, is as if the token is not gathered quickly enough the first time the request is done. I am not as good with async JS code conventions so I wonder if is something wrong with my approach.
I just want to be able to make each of these request actions one after another effectively and return back the result to the server I'm making the query from. Any insights on this would be greatly appreciated.
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
const express = require("express");
const bodyParser = require("body-parser");
const fetch = require("node-fetch");
const app = express();
const PORT = process.env.PORT;
app.use(bodyParser.json());
const url = "https://example.com:9443/";
const credentials = { username: "ubnt", password: "ubnt" };
let status;
let token;
async function postData(url = '', path = '', body = {}) {
const response = await fetch(`${url}${path}`, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
Referer: url,
accept: "application/json, text/plain, */*",
"accept-language": "en-US,en;q=0.9,es;q=0.8",
"content-type": "application/json;charset=UTF-8",
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: JSON.stringify(body)
}).then(response => response.json().then(json => ({
authToken: response.headers.get("x-auth-token"),
json
})))
return response;
}
async function getData(url = '', path = '', token = '') {
const response = await fetch(`${url}${path}`, {
method: 'GET',
mode: 'cors',
cache: 'no-cache',
headers: {
Referer: url,
accept: "application/json, text/plain, */*",
"accept-language": "en-US,en;q=0.9,es;q=0.8",
"content-type": "application/json;charset=UTF-8",
"x-auth-token": token
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: null
}).then(response => response.json())
return response;
}
const statusHandler = (req, res) => {
const {
seed
} = req.body.input;
postData(url, 'api/v1.0/user/login', credentials)
.then(response => {
console.log(response.authToken);
token = response.authToken;
}).then(getData(url, 'api/v1.0/interfaces', token)
.then(response => {
console.log(response[0].status);
status = response[0].status;
}));
console.log(seed);
return res.json({
status,
});
};
app.post("/status", statusHandler);
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}...`);
});
Turns out I was using the functions incorrectly. I did not use an async function for the statusHandler. I was was not waiting for the reply from the api before proceeding with the next request. It seems that with the first requests the API responds a lot slower.
I fixed by waiting for a reply on each of the function declarations and their calls on the handler using async / await. Doing this allowed me to clean up the code a lot more making it more readable.
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
const express = require("express");
const bodyParser = require("body-parser");
const fetch = require("node-fetch");
const app = express();
const PORT = process.env.PORT;
app.use(bodyParser.json());
const url = "https://example.com:9443/";
const credentials = { username: "ubnt", password: "ubnt" };
let status;
let token;
async function postData(url = '', path = '', body = {}) {
const response = await fetch(`${url}${path}`, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
Referer: url,
accept: "application/json, text/plain, */*",
"accept-language": "en-US,en;q=0.9,es;q=0.8",
"content-type": "application/json;charset=UTF-8",
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: JSON.stringify(body)
})
const json = await response.json()
.then(json => ({
authToken: response.headers.get("x-auth-token"),
json
}));
return json
}
async function getData(url = '', path = '', token = '') {
const response = await fetch(`${url}${path}`, {
method: 'GET',
mode: 'cors',
cache: 'no-cache',
headers: {
Referer: url,
accept: "application/json, text/plain, */*",
"accept-language": "en-US,en;q=0.9,es;q=0.8",
"content-type": "application/json;charset=UTF-8",
"x-auth-token": token
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: null
})
const json = await response.json()
return json
}
const statusHandler = async (req, res) => {
const {
seed
} = req.body.input;
await postData(url, 'api/v1.0/user/login', credentials)
.then(response => {
console.log(response.authToken);
token = response.authToken;
})
await getData(url, 'api/v1.0/interfaces', token)
.then(response => {
console.log(response[0].status);
status = response[0].status;
});
console.log(seed);
return res.json({
status,
});
};
app.post("/status", statusHandler);
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}...`);
});

How to fix unsupported_grant_type error in nodejs request to Reddit API

I am receiving an unsupported_grant_type error when making a request to Reddit to obtain an access token. I am attempting to make an app that makes API requests without user context.
I've tried moving the parameters and headers around but to no avail.
This is the code I am currently using (note that I have username = 'my client ID' and password = 'my secret key'
var request = require("request");
var auth = 'Basic ' + Buffer.from(username + ':' + password).toString('base64');
var options = { method: 'POST',
url: 'https://www.reddit.com/api/v1/access_token',
headers:
{ 'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': auth,
},
body:
JSON.stringify({grant_type: 'client_credentials',
user: username,
password: password})
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(response,body);
});
I did resolve this problem to this way.
I change the header 'Content-Type': 'application/x-www-form-urlencoded' for Accept: 'application/json'.
The body doesn't make it a JSON Object, I send it as a string.
options.body = 'grant_type=client_credentials'
const options = {};
options.method = 'POST';
options.headers = new Headers({
'Accept-Language': 'en_US',
Accept: 'application/json',
Authorization: auth,
});
options.body = 'grant_type=client_credentials';

Node.js http - send GET data to server

How can I send data with GET method using https/http module? With POST everything works.
First code (GET):
var querystring = require('querystring'),
protocol = require('https');
var options = {
host: 'httpbin.org',
path: 'get',
method: 'GET',
headers: {},
port: 443
};
var data = querystring.stringify({
limit: 3
});
Object.assign(options.headers, {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Content-Length': Buffer.byteLength(data)
});
var req = protocol.request(options, response => {
response.setEncoding('utf8');
var end = '';
response.on('data', data => end += data);
response.on('end', () => console.log(end));
});
req.write(data);
req.end();
Response:
{
"args": {},
"headers": {
"Connection": "close",
"Content-Length": "7",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Host": "httpbin.org"
},
"origin": "31.0.120.218",
"url": "https://httpbin.org/get"
}
Second code (POST, I only replaced options object):
var options = {
host: 'httpbin.org',
path: 'post',
method: 'POST',
headers: {},
port: 443
};
Response:
{
"args": {},
"data": "",
"files": {},
"form": {
"limit": "3"
},
"headers": {
"Connection": "close",
"Content-Length": "7",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Host": "httpbin.org"
},
"json": null,
"origin": "31.0.120.218",
"url": "https://httpbin.org/post"
}
I will be very grateful for some help, now I don't know what I am doing wrong.
Your problem is that in a get, the query is appended to the path, as #Quy points out, get requests don't have a body. Without an understanding of how the server is set up, I would look at doing it like so:
var data = querystring.stringify({
limit: 3
});
var options = {
host: 'httpbin.org',
path: 'get?' + data,
method: 'GET',
headers: {},
port: 443
};

Node HTTP POST Request to Netatmo

I'm trying to do a http POST request to my Netatmo weatherstation's cloud using NodeJS. It really needs to be http post and not use the node's 'request' module, because I indend to use it in AWS Lambda and that module is currently not supported there.
Whatever I try, I get the dreaded {"error":"invalid_request"} with result 400.. I am at a loss as to what the problem is.
Here's my snippet:
var querystring = require('querystring');
var https = require('https');
function getNetatmoData(callback, cardTitle){
var sessionAttributes = {};
var shouldEndSession = false;
cardTitle = "Welcome";
var speechOutput ="";
var repromptText ="";
console.log("sending request to netatmo...")
var payload = querystring.stringify({
'grant_type' : 'password',
'client_id' : clientId,
'client_secret' : clientSecret,
'username' : userId,
'password' : pass,
'scope' : 'read_station'
});
var options = {
host: 'api.netatmo.net',
path: '/oauth2/token',
method: 'POST',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(payload)
};
//console.log('making request with data: ',options);
var req = https.request(options, function(res) {
res.setEncoding('utf8');
console.log("statusCode: ", res.statusCode);
console.log("headers: ", res.headers);
res.on('data', function (chunk) {
console.log("body: " + chunk);
});
res.on('error', function (chunk) {
console.log('Error: '+chunk);
});
res.on('end', function() {
speechOutput = "Request successfuly processed."
console.log(speechOutput);
repromptText = ""
//callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
});
});
req.on('error', function(e){console.log('error: '+e)});
req.write(payload);
req.end();
}
Here's Cloud9's console log:
Debugger listening on port 15454
sending request to netatmo...
statusCode: 400
headers: { server: 'nginx',
date: 'Tue, 24 Nov 2015 19:30:25 GMT',
'content-type': 'application/json',
'content-length': '27',
connection: 'close',
'cache-control': 'no-store',
'access-control-allow-origin': '*' }
body: {"error":"invalid_request"}
Request successfuly processed.
Aarrgh.. I oversaw something. Apparently, the headers need to be set in the options when making the request. It is in the headers variable that the Content-Type and Content-Length are set.
Without further adue, here is the correct working of the options variable :
var options = {
host: 'api.netatmo.net',
path: '/oauth2/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(payload)
}
};

Categories