I'm working on a Gnome-shell extension, and I'm stuck in using Soup to contact a server.
Basically, I want to send a POST request which can be performed after authentication. If authenticated, the response if a JSON content, and if not authenticated, the response content is HTML, the welcome page of the site.
Here is my code so far:
let session = new Soup.SessionSync();
session.user_agent = Me.metadata.uuid;
let authParams = {'login': 'xxx', 'password': 'xxx'};
let message = Soup.form_request_new_from_hash('POST', authURL, authParams);
session.queue_message(message, Lang.bind(this, function(session, response) {
global.log('AUTHENTICATE: ' + response.status_code + ' - ' + response.reason_phrase);
global.log('AUTHENTICATE: ' + response.response_body.data);
let msg = Soup.form_request_new_from_hash('POST', url, params);
session.queue_message(msg, Lang.bind(this, function(session, response) {
global.log(response.status_code + ' - ' + response.reason_phrase);
global.log(response.response_headers.get_one('content-type'));
}));
}));
I can see that the authentication request works well, according to the response, but the second requests returns me the HTML content.
I checked in SoapUI by sending these 2 requests, and it works well (I get some JSON content) when I set the option "Maintain HTTP Session".
Therefore, I'm thinking that my session doesn't store the authentication when the second request is sent. Do you know what I am missing in here, in order to have the authentication saved in my session?
I alternatively tried to use some tips from the following link, but without success: Consume a webservice with basic authentication using Soup
Thanks in advance for your help.
let session = new Soup.SessionSync();
session.user_agent = Me.metadata.uuid;
let authParams = {'login': 'xxx', 'password': 'xxx'};
let message = Soup.form_request_new_from_hash('POST', authURL, authParams);
session.queue_message(message, Lang.bind(this, function(session, response) {
global.log('AUTHENTICATE: ' + response.status_code + ' - ' + response.reason_phrase);
global.log('AUTHENTICATE: ' + response.response_body.data);
let msg = Soup.form_request_new_from_hash('POST', url, params);
session.queue_message(msg, Lang.bind(this, function(session, response) {
global.log(response.status_code + ' - ' + response.reason_phrase);
global.log(response.response_headers.get_one('content-type'));
}));
}));
Related
All,
I am trying to figure out how to pass the results from an https.request in node.js code out to a variable. I have an https.request setup that correctly passes the correct information to a SOAP API and gets the correct response back. My ultimate goal is to get the output from the https.request into a variable that I can call using Express.
Here is are my code chunks.
HTML:
<div class="row">
<div class="col-md-12" class="pull-left">
<p> TEST </p>
<p>{{soapreply}}</p>
</div>
JS:
app.post('/cucmmapper/submit', function (req, res) {
// FORM - DATA COLLECTION
var cucmpub = req.body.cucmpub;
var cucmversion = req.body.cucmversion;
var username = req.body.username;
var password = req.body.password;
var authentication = username + ":" + password;
var soapreplyx = '';
// SOAP - BUILD CALL
var https = require("https");
var headers = {
'SoapAction': 'CUCM:DB ver=' + cucmversion + ' listCss',
'Authorization': 'Basic ' + new Buffer(authentication).toString('base64'),
'Content-Type': 'text/xml; charset=utf-8'
};
// SOAP - AXL CALL
var soapBody = new Buffer('<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.cisco.com/AXL/API/11.5">' +
'<soapenv:Header/>' +
'<soapenv:Body>' +
'<ns:listCss sequence="?">' +
'<searchCriteria>' +
'<name>%</name>' +
'</searchCriteria>' +
'<returnedTags uuid="?">' +
'<name>?</name>' +
'<description>?</description>' +
'<clause>?</clause>' +
'</returnedTags>' +
'</ns:listCss>' +
'</soapenv:Body>' +
'</soapenv:Envelope>');
// SOAP - OPTIONS
var options = {
host: cucmpub, // IP ADDRESS OF CUCM PUBLISHER
port: 8443, // DEFAULT CISCO SSL PORT
path: '/axl/', // AXL URL
method: 'POST', // AXL REQUIREMENT OF POST
headers: headers, // HEADER VAR
rejectUnauthorized: false // REQUIRED TO ACCEPT SELF-SIGNED CERTS
};
// SOAP - Doesn't seem to need this line, but it might be useful anyway for pooling?
options.agent = new https.Agent(options);
// SOAP - OPEN SESSION
var req = https.request(options, function (res) {
res.setEncoding('utf8');
res.on('data', function (d) {
soapreplyx = d;
console.log("Got Data: " + d);
});
});
// SOAP - SEND AXL CALL
req.write(soapBody);
res.render('cucmmapper-results.html'), {
'title': 'CUCM 2.1',
'soapreply': soapreplyx
};
req.end();
req.on('error', function (e) {
console.error(e);
});
});
}
The line "console.log("Got Data: " + d)" is getting the correct expected reply from the API, however, I can't figure out how to get that data into my variable "soapreplyx" which changes in Express to "soapreply".
Much appreciated for any help you might have!
You're not waiting for your request to respond before you call res.render(), so the value of soapreplyx is always '', its initial value. To correct this, add an 'end' event listener on the response object passed to your https.request() callback.
You're not appending the chunks of the response to your soapreplyx variable, you're reassigning its value with each successive chunk.
let soapRequest = https.request(options, soapResponse => {
soapResponse.on('data', chunk => {
soapreplyx += chunk
})
soapResponse.on('end', () => {
return res.render('cucmmapper-results.html', {
title: 'CUCM 2.1',
soapreply: soapreplyx
})
})
})
soapRequest.write(soapBody)
soapRequest.end()
I'm a front end developer trying to do backend stuff using express js, so this might be an easy one.
This is my problem:
I want a user to login using Facebook Social Login.
I have manage to set up the front end part so I have a:
* access_token
* app_id
* app_secret
Now I want to get a refresh-token on my backend, it looks like this:
var app_id = '122345abcd';
var app_secret = '111xxxx';
var accessToken = req.body.accessToken;
var baseURL = 'https://graph.facebook.com';
var url = baseURL + '/oauth/access_token?' +
'grant_type=fb_exchange_token&' +
'client_id=' + app_id + '&' +
'client_secret=' + app_secret + '&' +
'fb_exchange_token=' + accessToken;
request({
url: url,
json: true
}, function(error, response, body) {
console.log(body.error.message);
});
This generates a very nice URL that looks right. It can look like this:
https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&
client_id=122345abcd&
client_secret=111xxxx&
fb_exchange_token=EAACjzsDAL4oBALxjCRB5cTYTIdyTVYyg6z6do4IoN8R9b65dtGR7DCMvLNr4cRjeTxZBH7SGH0PNqLL5wfxf73lGORjkD0ZCP5c6WTkdTnCfvAPUZCTouRtTYY89UAgZAxO8GgGpjzZCSwB7W95YCVHKDKpNrcTH5R93bInV0UQZDZD
But what I get back is this.
"Missing client_id parameter."
What have I missed?
There's clearly and client_id in the url, even though it's the app_id. But that seems to be right.
I have followed this guide:
https://developers.facebook.com/docs/facebook-login/access-tokens/expiration-and-extension
UPDATE: The guide above say you shall use & in the URL. That is what breaks things. Use & instead.
var url = baseURL + '/oauth/access_token?' +
'grant_type=fb_exchange_token&' +
'client_id=' + app_id + '&' +
'client_secret=' + app_secret + '&' +
'fb_exchange_token=' + accessToken;
So, I want to request something from external api that is using TOTP. I have everything set up the right way and! On my localhost everything works fine, but on remote server - VPS - it crashes and gives following error :
{
"status" : "success",
"data" : {
"error_message" : "API access enabled, but unable to verify two-factor authentication code. If you need help with this, please contact support#bitskins.com."
}
}
HTML :
// Secret and keys for Bitskins API
var totp = new TOTP('MPKXR23QBRQVW7SZ');
var codeFromSecret = totp.now();
var url = 'https://bitskins.com/api/v1/get_item_price/?api_key=' + bitskins.apikey + '&code=' + totp.now() + '&names=' + namesHashed + '&delimiter=!END!';
console.log("url " + url);
request(url, function (error, response, body) {
if (!error) {
var json = JSON.parse(body);
console.log("body " + body);
console.log("json " + json);
for (var a = 0; a < items.length; a++) {
itemPrices.push(json.data.prices[a].price);
}
console.log('about to emit pricesFetched # steambot.js');
lastStep(itemPrices);
}
});
I am trying to make an API request to fitbit, using the oauth debugger from fitbit (https://dev.fitbit.com/apps/oauthtutorialpage) i am trying to figure out what i am doing wrong. I have added comments to my code below to help you understand what i am trying to achieve. What i am quite sure of is that i am either signing my request wrong, or using the wrong data to sign it. This is echoed by the API response.
I know there are more fitbit api questions here on stackoverflow, however did not find my answer there.
Is there anyone with more experience in Oauth signatures that knows what i could be doing wrong? Or could help me find a different approach to this?
var request = require('request');
var crypto = require('crypto');
var params = {
'oauth_consumer_key' : 'key12345',
'oauth_nonce' : Math.random().toString(36).substring(3), //random string
'oauth_signature_method' : 'HMAC-SHA1',
'oauth_timestamp' : Date.now().toString().substring(0,10), //timestamp with the same length as in the tutorial
'oauth_version' : '1.0'
}
var oauth_consumer_secret = 'secret123';
var post_string = 'POST&https://api.fitbit.com/oauth/request_token';
for(var key in params){
post_string += '&' + key + '=' + params[key];
}
/*At this point we have made a post string that we have to hash with hmac-sha1
the post string looks like this:
POST&https://api.fitbit.com/oauth/request_token&oauth_consumer_key=key12345&oauth_nonce=az6r8cqlzyqfr&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1439147378&oauth_version=1.0
The post_string from the tutorial looks like this:
POST&%2Foauth%2Frequest_token&oauth_consumer_key%3D%26oauth_nonce%3D%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1439145944%26oauth_version%3D1.0
*/
var hmac = crypto.createHmac('sha1', oauth_consumer_secret + "&");
// The tutorial page shows me the signature was 'signed with secret&'. I have tried with and without the & at the end, but without luck.
hmac.setEncoding('base64'); //i'm not sure if this is correct
hmac.write(post_string);
hmac.end();
var hash = hmac.read();
//and finally adding the hash to the parameters.
params.oauth_signature = hash;
//now, making the request with an authorization header.
var header='';
for (var key in params){
if(header.length === 0){
header = ' OAuth ' + key + '="' + params[key] + '"';
}
else{
header += ', ' + key + '="' + params[key] + '"';
}
}
/*
At this point the header parameter looks like this
OAuth oauth_consumer_key="key12345", oauth_nonce="jnr97ppvjs2lnmi", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1439148049", oauth_version="1.0", oauth_signature="random_signature"
The tutorial tells me to use the headers:
OAuth oauth_consumer_key="key12345", oauth_nonce="jnr97ppvjs2lnmi", oauth_signature="different_signature", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1439145944", oauth_version="1.0"
*/
var headers ={
'Authorization' : header
}
var url="https://api.fitbit.com/oauth/request_token";
var requestTimeout = 5000;
var opts = {
url: url,
timeout: requestTimeout,
headers : headers
}
request(opts, function (err, res, body) {
if (err) {
console.dir(err);
return;
}
var statusCode = res.statusCode;
if(res.statusCode === 200){
console.log(body);
}
else{
console.log("http-error-code: " + res.statusCode);
console.log(body);
}
})
/*
The response:
http-error-code: 401
{"errors":[{"errorType":"oauth","fieldName":"oauth_signature","message":"Invalid signature: 9fXI85C7GvZqMyW1AK1EkOSWZCY="}],"success":false}
*/
To get access token and secret use Grant (you can test FitBit in the playground).
Once you have access token and secret use Purest to make subsequent request to the FitBit API.
Here is an example on how to get the user's profile:
var Purest = require('purest')
var fitbit = new Purest({provider:'fitbit',
key:'[CONSUMER_KEY]', secret:'[CONSUMER_SECRET]'})
fitbit.get('user/-/profile', {
oauth:{token:'[ACCESS_TOKEN]', secret:'[ACCESS_SECRET]'}
}, function (err, res, body) {})
Alternatively you can use request for that:
var request = require('request')
request.get('https://api.fitbit.com/1/user/-/profile.json', {
oauth:{
consumer_key:'..',
consumer_secret:'..',
token:'..',
token_secret:'..'
}
}, function (err, res, body) {})
In short - don't try to implement the web server OAuth flow by yourself - use Grant, then use either Purest or request, just keep in mind that you don't have to pass all of the OAuth parameters by yourself, just pass the credentials.
I have recently set up node.js using Express and I created a simple HTML form using Jade. The form is to insert the data in a PostgreSQL database. The problem is that when I press submit on the form, everything is inserted on the database, but the HTML form is just hanging/lingering, and at some point it stops with No data received, ERR_EMPTY_RESPONSE. Sometimes it also inserts the data twice. I guess this is because the server side does not return a response, but I cannot see how (I am new to node.js).
The form has action="add_device" which is routed to routes/add_device.js. add_device.js looks like this:
var express = require('express');
var router = express.Router();
router.get('/', function(request, response, next) {
res.send('Nothing to see here. Move along.');
});
router.post('/', function(request, response, next) {
var db = require('../public/javascripts/db/insert');
var result = db.insertDevice(request, response);
return result;
});
module.exports = router;
The insertDevice function in my db module looks like this (it is exported with module.exports):
// Insert new QA device. Data arriving as a request from a HTML form.
insertDevice: function (request, response) {
// Input that is verified in the HTML form.
// Convert to proper format for PostgreSQL query.
var name = '\'' + request.body.name + '\'';
var ip_address = '\'' + request.body.ip_address + '\'';
var os = '\'' + request.body.os + '\'';
// Input that needs to be verified. Prepare for PostgreSQL query.
var mac_address;
var os_version;
request.body.mac_address == "" ? mac_address = 'NULL' : mac_address = '\'' + request.body.mac_address + '\'';
request.body.os_version == "" ? os_version = 'NULL' : os_version = '\'' + request.body.os_version + '\'';
var pg = require('pg'); // PostgreSQL module.
var td = require('./table_data') // Database constants.
var client = new pg.Client(request.app.get('postgreConnection'));
client.connect(function(err) {
if (err) {
return console.error('Could not connect to postgres', err);
}
var QUERY = "INSERT INTO " + td.QA_DEVICES.TABLE_NAME + "(" +
td.QA_DEVICES.COLUMN_NAME + ", " +
td.QA_DEVICES.COLUMN_MAC_ADDRESS + ", " +
td.QA_DEVICES.COLUMN_IP_ADDRESS + ", " +
td.QA_DEVICES.COLUMN_OS + ", " +
td.QA_DEVICES.COLUMN_OS_VERSION + ") VALUES(" +
name + ", " +
mac_address + ", " +
ip_address + ", " +
os + ", " +
os_version + ");";
client.query(QUERY, function (err, result) {
if (err) {
return console.error('Error running query: ' + QUERY, err);
}
console.log('Query performed: ' + QUERY);
client.end();
});
});
}
The 'Query performed' is always logged to console and data inserted into the database, but the form is still hanging. My questions are:
Is it the lack of response from the server that makes the form hang?
How can I "send a response back" to the front end?
Is it possible to route the front end to another page after insertion into the database? What is the best practice?
Yes, your request is receiving no response, so it is hanging.
In order to send a response, you can either send a blind acknowledgement right when the request is received (that is not dependent upon the success of the query and may be bad practice), or you can send it in the callback.
client.query(QUERY, function (err, result) {
if (err) {
// response.json({status: 'error'});
response.write('Error');
return console.error('Error running query: ' + QUERY, err);
} else {
// You can send json here too
// response.json({status: 'success'});
response.write('Success');
}
console.log('Query performed: ' + QUERY);
client.end();
});
If you want to go to another page, simply parse the incoming response on the client side and do a redirect. Using json is a good way to carry this out. You can also do a response.redirect(url) on the server side too, instead of sending back data. Have fun