Thanks for your help in advance.
I am trying to solve a problem that has bugged me pretty much for a few weeks now on and off (tried to solve it myself) and i am pretty stuck.
Basically i have a scenario where i am sending a data call to a identity server (Oauth 2.0) to retrieve a token that is then used in a API call.
To do this, i have setup two calls to the request module in NodeJS. The first call queries the server whilst the second call needs to use the token retrieved in the first to access the API i am accessing.
I cannot figure out how to get the variable from the first call (the token) to be accessible to the second request. I keep getting that it is undefined from the console.
This is the code i have tried so far. Relevant variable. Trying to take variable token through to request 2 as i need that token for the api call. I am running these requests in a function called dostuff.
var request = require('request');
var http = require('http');
var fs = require('fs');
var options = {
url: 'https://identitywebsite.com/connect/token',
headers: {'authorization' : 'basic <<KEY>>', 'Content-Type' : 'application/x-www-form-urlencoded'},
port: '443',
path: '/users',
method: 'POST',
body: 'grant_type=password&username=<<USERNAME>>&password=<<PASSWORD>>&scope=read+write'
var dostuff = function() {
request(options, function(error, response, body){
console.log("success");
Data = JSON.parse(body);
//console.log(xx);
global.token = data.access_token;
})
};
var options2 = {
url: 'https://apiwebsite.com',
headers: {'authorization' : 'Bearer ' + token, 'Content-Type' : 'application/json'},
port: '443',
path: '/users',
method: 'GET'
}
console.log(options2);
request(options2, function(error, response, apiresponse){
console.log("triggered");
console.log(" data success2");
apidata = JSON.parse(apiresponse);
console.log(apidata);
});
Well a few things you should do. One is use some promises. Also do your best to not use global variables.
Also you are missing a closing bracket on your first options.
Promises are really neat though, as you can do things like this.
do_something(options)
.then(function(my_data){
var options2 = {
url: 'https://apiwebsite.com',
headers: {'authorization' : 'Bearer ' + my_data, 'Content-Type' : 'application/json'},
port: '443',
path: '/users',
method: 'GET'
}
do_something_else(options2)
.then(function(my_other_data){
//do stuff with my_other_data
}
}
Use this library! It makes doing promises with requests really easy!
https://github.com/request/request-promise, you could also do this in bluebird which is my favorite of all the promise libraries, but it is slightly more complex.
Edit:
Since someone posted that this is off topic, which it isn't. I will go farther in depth of why this is not working the way you think it should. One of the reasons this is not working quite right is Node.JS is asynchronous.
What this means is your code is not waiting for your first request to finish before running your second request so you are not actually getting the token before your next request needs this.
One of the ways around this is to use promises like I said originally. There are plenty of libraries out there that force synchronicity. There is this library for example:
https://github.com/yortus/asyncawait
and many many more.
You could abstract this farther and call your first request from your second request as well to grab the API key and store it.
Related
I was just wondering if someone could help me figure this out. My code to get the access token is:
const inputBody = 'client_id=00000000-0000-0000-0000-000000000001&secret=mySecretPassword';
const headers = {
'Content-Type':'application/x-www-form-urlencoded',
};
fetch('https://api.replicastudios.com/auth',
{
method: 'POST',
body: inputBody,
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
I then need to include the access token received from the first call in order to insert it below.
const headers = {
'Authorization':'Bearer {token}'
};
fetch('https://api.replicastudios.com/voice',
{
method: 'GET',
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
What is the best way to save the access token and insert it into the second request?
Please, and one more time, please don't store users security tokens in session storage, local storage, cookies (by yourself, let browser do it securely for you), etc.
Browsers have pretty awesome way of handling those security sensitive stuff, and those ways are preventing session hijacking through XSS and bunch of other stuff.
Proper way of doing things:
On the frontend you call your login (or in your case) auth route, and from the backend instead of sending your security token in body you need to set it in the secure cookie. One of the common ways to do it with Node+Express is like this:
res.status(200)
.cookie('auth_cookie', token, { httpOnly: true, maxAge: 86400 * 1000, sameSite: 'none', secure: true})
.json({ success: true, message });
This was one example of it, you should learn what all those arguments do here: https://developer.mozilla.org/en-US/docs/Tools/Storage_Inspector/Cookies
And on the last part, you dont need to manually include those cookies with each other request after that, if you are using Axios you can just put { withCredentials: true } and it's gonna automatically include those secured tokens in the cookie part of your request. There is a good thread already on that here: Make Axios send cookies in its requests automatically
I understand that this can seem as much work but it will make web safe(er) and it promotes good practices.
If your code is in a browser which it seems like it is, you could save the access token to session storage for use in future calls. When the tab is closed, session storage will be cleared.
const inputBody = 'client_id=00000000-0000-0000-0000-000000000001&secret=mySecretPassword';
const headers = {
'Content-Type':'application/x-www-form-urlencoded',
};
fetch('https://api.replicastudios.com/auth',
{
method: 'POST',
body: inputBody,
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
// NEW CODE
// body.access_token is my guess, you might have to change this
// based on the response
window.sessionStorage.setItem('access_token', body.access_token)
});
Then you can get the access token using:
window.sessionStorage.getItem('access_token')
I am building an app using Node.js. I am using the request package to make server-side GET requests. Specifically, I am making requests that use custom HTTP headers:
https://www.npmjs.com/package/request#custom-http-headers
The documentation shows how to make one request at a time. However, I need to make a request to two different API's. Does anyone have any idea how to do this? My current code for making one request:
var cookie = parseCookie.parseCookie(req.headers.cookie);
var cookieText = 'sid='+cookie;
var context;
function callback(error, response, body) {
var users = JSON.parse(body);
res.render('../views/users', {
context: users
});
}
var options = {
url: 'http://localhost:3000/api/admin/users/',
headers: {
host: 'localhost:3000',
connection: 'close',
cookie: cookieText
}
};
request(options, callback);
//need to make a request to another API.
As a side note, the reason I need to use custom HTTP headers is so I can include a cookie so my API can authenticate.
For flow control in nodejs, I would recommend you to use async
If you want to do in parallel without order of execution :
async.parallel([
function(callback) { request(apiCall1Options, callback); },
function(callback) { request(apiCall2Options, callback); }
], function(err, apiCallResults) { console.log(apiCallResults) })
If you need order, use async.waterfall.
Could be done as well by simply using plain callbacks, which I wouldnt recommend you using, or a promise library, like Q, or bluebird.
I am trying to program the following Curl command using Angular JS. I'm very new to AngularJS, so I may be missing something fundamental.To be specific, this is a mobile app, that is using the ionic framework (which uses Angular JS)
The curl command is this:
curl -X POST -d "username=xxxx&password=xxxx&action=login&view=console" http://myserver.com/zm/index.php?skin=xml
This works perfectly from command line.
In AngularJS, I have the following code:
angular.module('starter.controllers').controller('MonitorCtrl', function($ionicPlatform, $scope,$http)
{
console.log("***MAKING REQUEST");
//$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
$http({
url:'http://myserver.com:9898/zm/index.php?skin=xml',
method:'post',
headers: {'Content-Type': 'application/x-www-form-urlencoded',
'Accept': '*/*',
},
transformRequest: function(obj) {
var str = [];
for(var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
var foo= str.join("&");
console.log ("****RETURNING "+foo);
return foo;
},
data: {username:'admin',
password:'xxxx',
action:'login',
view:'console'}
})
.success (function()
{console.log("****YAY");
})
.error(function(data, status, headers, config)
{
console.log("***OOPS "+status + " H: "+data);
});
});
When I run this code, I see a POST request going to my server (I am sniffing using httpry) and a 200OK being returned from the server with the required payload, but my success handler is not being called. The error handler is called with a status of 0
data should be a json. It depends on your server.
The common way to do it is is using properties on an object:
data: {username: 'xxxx', password: 'xxxx', action: 'login', view:'console'}
but if your server receives a string as an input then:
data: {key: 'username=xxxx&password=xxxx&action=login&view=console'}
Okay, I finally figured it all out
a) Amir popovich is right in that if I am dealing with XML, I need to 'transmogrify' (transform in a magical manner, for non Calvin and Hobbes readers) JSON to XML. The updated code I posted in my question does exactly that and works. But even after doing all that, my URL post (or get) was failing.
b) The entire darn CORS error I was facing was because I was running this app in a desktop browser (Safari) and it was Safari that was kicking out the response unless it had Accept Origin
c) When I deployed and tested the app on my phone, it worked like a charm. Don't ask me why
I am using the node.js request module to post a large POST request (~150MB) to a REST service. For any request bigger than about 30MB, it seems to hang. My guess is that it is doing some naive JSON.stringify()ing to the data instead of streaming it, and once it gets large enough to hit swap, it just becomes very slow.
This is what my request looks like:
request({
uri: url
method: 'post',
json: args, // args is a 150MB json object
}, function(err, resp, body) {
// do something
});
The same request made using angularjs's $http from within a browser works in less than a minute, so I know it's the cilent and not the server.
Is there an easy way to fix this?
Streaming is not going to happen automatically. The API to stream something and the API to send a complete buffer are almost always different, and this is no exception. Most libraries out there given a complete javascript object in memory are just going to JSON.stringify it because you've already paid the memory and I/O price to load it in RAM so why bother streaming?
You could try the oboe streaming JSON library, which specializes in this type of thing. Here's a working example:
var oboe = require("oboe");
var args = {foo: "bar"};
var url = "http://localhost:2998";
oboe({url: url, body: args, method: "post"}).on("done", function (response) {
console.log('response:', response);
});
Aside: instead of guessing, you could verify in the source exactly what is happening. It's open source. It's javascript. Go ahead and dig in!
Updating answer:
I'd suggest your try two things :
See how much time superagent is taking to post the same data. Superagent is as simple as request.
var request = require('superagent');
request
.post(url)
.send(args)
.set('Accept', 'application/json')
.end(function(error, res){
});
Compress the data to be posted by using zlip, your will be compressing the data into a zlip buffer and write that as your output
var zlib = require('zlib');
var options = {
hostname: 'www.yourwebsite.com',
port: 80,
path: '/your-post-url',
method: 'POST',
headers: {'Content-Encoding': 'gzip'} // tell the server that the data is compressed
};
//args is your JSON stringified data
zlib.gzip(args, function (err, buffer) {
var req = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
// ... do stuff with returned data
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
req.write(buffer); // send compressed data
req.end();
});
My code is attempting to post data to a Coldfusion API from my local Node.js server. I have managed to communicate with the API and authenticate myself via the request headers. However I am having difficulty actually passing my JSON object through as I cannot get the structure right.
The API does not accept the JSON option of the request module, so that is my easiest option out of the window.
The API is expecting the following:
{
'source': {
'customer': {
'customerlogin': 'myusername',
'customerpassword': 'mypassword',
}
}
}
my code works if I hard code the following body parameter (from a sucessful post by somebody else) into my post.
var Jrequest = require('request');
var options = {
uri: 'http://myAPI/customerSSO.json',
headers: {'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': something', 'Timestamp': timestamp},
method: 'POST',
body: 'source=%7B%0D%0A++%22customer%22%3A+%7B%0D%0A++++%22customerlogin%22%3A+%22myusername%22%2C%0D%0A++++%22customerpassword%22%3A+%22mypassword%22%2C%0D%0A%09%22success%22%3A+%22%22%0D%0A++%7D%0D%0A%7D' // Working
};
Jrequest(options, function (error, response, body){
res.send(body);
});
If I send the JSON through in other ways, for example json.stringify(), it is rejected on the grounds that 'source is required but not defined'.
So I suppose my question is, in node.js how do I turn JSON into something that looks like this
'source=%7B%0D%0A++%22customer%22%3A+%7B%0D%0A++++%22customerlogin%22%3A+%22myusername%22%2C%0D%0A++++%22customerpassword%22%3A+%22mypassword%22%2C%0D%0A%09%22success%22%3A+%22%22%0D%0A++%7D%0D%0A%7D'
or have I overlooked another option?
Thanks for any help and apologies if I have used incorrect terminology.
I think this should work:
var querystring = require('querystring');
...
request({
...
headers : { 'Content-Type': 'application/x-www-form-urlencoded', ... },
body : 'source=' + querystring.escape(JSON.stringify({
'customer': {
'customerlogin': 'myusername',
'customerpassword': 'mypassword',
}
})),
...
}, ...)
Your example also contains newlines and carriage returns and such, but I'm assuming those are optional.