How is Node's crypto.pbkdf2() supposed to work? - javascript

I've read the documentation many times for the Node crypto module's pbkdf2() function. A question I asked previously was collapsed without much thought - so let me say this: I think that I have a lack of understanding about the callback - but I have read many resources trying to truly understand it - YDKJS Async, MDN, "Learning JavaScript" by O'Reilly.
I have a console.log statement within an else clause in the callback that is logging appropriatey, so I'm certain that the callback is being executed, although my debugging program (in VSCode) isn't halting execution.
I tried two different things, as seen in the code below: one was to declare a variable and change its value to derivedKey.toString('hex') within the else clause, and the other was to return the the derivedKey.toString('hex'). Neither worked.
I tried chaining a then clause, but crypto.pbkdf2 returns void and "property 'then' does not exist on type 'void'".
Here is the written method:
private static async hashPassword(password:string, salt:string):Promise<string> {
var hashedPassword;
const iterations = 50000;
const keylen = 64;
const digest = 'sha512';
const possibleReturnedValue = await crypto.pbkdf2(password, salt, iterations, keylen, digest, (err, derivedKey) => {
if (err) {throw err;}
else {
console.log(derivedKey.toString('hex'));
console.log("Hey now");
hashedPassword = derivedKey.toString('hex');
return derivedKey.toString('hex');
}
})
return hashedPassword;
}
What it really comes down to is this: I don't know how to get the derivedKey.toString('hex') value out of a function that returns 'void' with the callback.

Your problem is that the crypto.pbkdf2 function is a bit old, and does not work with promises but using callbacks. So in order to use this function in modern asynchronous code it will be necessary to wrap that function in a Promise object.
The key idea is to call the resolve and reject function given by the promise's constructor in the callback.
Refactored to return a promise, your function will look like this:
function hashPassword(password:string, salt:string):Promise<string> {
return new Promise((resolve, reject) => {
const iterations = 50000;
const keylen = 64;
const digest = 'sha512';
crypto.pbkdf2(password, salt, iterations, keylen, digest, (err, key) => {
if (err) {
reject(err);
} else {
resolve(key.toString('hex'));
}
})
});
}

Related

Redis is outputting true instead of the desired value

I have nodejs running and I want to call this function:
function name(id) {
var x = rclient.get(id, function(err, reply) {
return reply;
});
return x;
}
however when I try to get the output of the function with console.log(name(1)) the output is true, instead of the value stored on the redis server. this seems like a simple thing to fix, however, it has me stumped.
Well you're using callbacks so the return value inside your callback function won't be returned to x.
Try this (depending on your redis client, I've assumed you use node-redis):
function name(id) {
return new Promise((resolve, reject) => {
rclient.get(id, function (err, reply) {
if (err) {
return reject(err);
}
resolve(reply);
});
});
}
// call with
name(1).then((value) => console.log(value)).catch((err) => console.log(err));
// better yet (inside async function)
async main() {
const value = await name(1);
}
Or, do yourself a favour and use handy-redis (https://www.npmjs.com/package/handy-redis):
async function name(id) {
return rclient.get(id);
}
// call with the same as above
Essentially, you're getting slightly confused with async/sync calls. The fact x resolves to true is likely the implementation of the .get method, and not the callback.
instead of the value stored on the redis server. this seems like a simple thing to fix, however, it has me stumped.
I felt like you when I first started with Node.js, it's odd compared to most languages, however, you'll soon find it more natural (especially with async/await syntax)

How to avoid an infinite loop in JavaScript

I have a Selenium webdriverIO V5 framework. The issue I am facing here is, the below code works fine on Mac OS, but it does not work correctly on the Windows OS. In the Windows OS it gets stuck with an infinite loop issue.
The below code functionality is: Merge yaml files (which contains locators) and return the value of the locator by passing the key:
const glob = require('glob');
const yamlMerge = require('yaml-merge');
const sleep = require('system-sleep');
let xpath;
class Page {
getElements(elementId) {
function objectCollector() {
glob('tests/wdio/locators/*.yml', function (er, files) {
if (er) throw er;
xpath = yamlMerge.mergeFiles(files);
});
do {
sleep(10);
} while (xpath === undefined);
return xpath;
}
objectCollector();
return xpath[elementId];
}
}
module.exports = new Page();
Since you are waiting on the results of a callback, I would recommend returning a new Promise from your getElements function and resolve() the value you receive inside the callback. Then when you call getElements, you will need to resolve that Promise or use the await notation. The function will stop at that point and wait until the Promise resolves, but the event loop will still continue. See some documentation for more information.
I'll write an example below of what your code might look like using a Promise, but when you call getElements, you will need to put the keyword await before it. If you want to avoid that, you could resolve the Promise from objectCollector while you're in getElements and remove the async keyword from its definition, but you really should not get in the way of asynchronous JavaScript. Also, you can probably shorten the code a bit because objectCollector looks like an unnecessary function in this example:
const glob = require('glob')
const yamlMerge = require('yaml-merge')
const sleep = require('system-sleep')
let xpath
class Page {
function async getElements(elementId) {
function objectCollector() {
return new Promise((resolve,reject) => {
glob('tests/wdio/locators/*.yml', function (er, files) {
if (er) reject(er)
resolve(yamlMerge.mergeFiles(files))
})
})
}
let xpath = await objectCollector()
return xpath[elementId]
}
}
module.exports = new Page();

Can't promisify callback based function

I want to use the library astro-js where a typical call in their docs looks like this:
const aztroJs = require("aztro-js");
//Get all horoscope i.e. today's, yesterday's and tomorrow's horoscope
aztroJs.getAllHoroscope(sign, function(res) {
console.log(res);
});
For several reasons, I would like to use it using async/await style and leverage try/catch. So I tried promisify like this:
const aztroJs = require("aztro-js");
const {promisify} = require('util');
const getAllHoroscopeAsync = promisify(aztroJs.getAllHoroscope);
async function handle() {
let result, sign = 'libra';
try {
result = await getAllHoroscopeAsync(sign);
}
catch (err) {
console.log(err);
}
console.log("Result: " + result);
}
However, when I log result it comes as undefined. I know the call worked since the library is automatically logging a response via console.log and I see a proper response in the logs.
How can I "await" on this call? (even by other means if this one is not "promisifyable")
util.promisify() expects the callback function to accept two arguments, the first is an error that must be null when there is no error and non-null when there is an error and the second is the value (if no error). It will only properly promisify a function if the callback follows that specific rule.
To work around that, you will have to manually promisify your function.
// manually promisify
aztroJs.getAllHoroscopePromise = function(sign) {
return new Promise(resolve => {
aztroJs.getAllHoroscope(sign, function(data) {
resolve(data);
});
});
};
// usage
aztroJs.getAllHoroscopePromise(sign).then(results => {
console.log(results);
});
Note, it's unusual for an asynchronous function that returns data not to have a means of returning errors so the aztroJs.getAllHoroscope() interface seems a little suspect in that regard.
In fact, if you look at the code for this function, you can see that it is making a network request using the request() library and then trying to throw in the async callback when errors. That's a completely flawed design since you (as the caller) can't catch exceptions thrown asynchronously. So, this package has no reasonable way of communicating back errors. It is designed poorly.
Try custom promisified function
aztroJs.getAllHoroscope[util.promisify.custom] = (sign) => {
return new Promise((resolve, reject) => {
aztroJs.getAllHoroscope(sign, resolve);
});
};
const getAllHoroscopeAsync = util.promisify(aztroJs.getAllHoroscope);
You could change your getAllHoroscopeAsync to a promise function
Example:
const getAllHoroscopeAsync = (sign) =>
new Promise(resolve =>
aztroJs.getAllHoroscope(sign, (res) => resolve(res)));

Return value from a mongodb query from nodejs

EDIT
OK I read here."You can't usefully return with asynchronous functions. You'll have to work with the result within the callback. This is due to the nature of asynchronous programming: "exit immediately, setting up a callback function to be called sometime in the future. And, at least with the current standard of ECMAScript 5, you can't get around this. As JavaScript is single-threaded, any attempt to wait for the callback will only lock up the single thread, keeping the callback and the return user forever pending in the event queue."
Is this still the case today?
ORIGINAL QUESTION
I have a problem accessing my variable outside the function in my node.js application.
const url = "mongodb://localhost:27017/";
getAllSampleTypes();
// I would like to have the variable "requested" accessible here
function getAllSampleTypes() {
MongoClient.connect(url, function (err, db) {
var dbo = db.db("myDb");
dbo.collection("data").distinct("sample_type", {}, (function (err, requested) {
// variable "requested" is accessible here
})
);
});
}
I tried with async/await but I still have the same problem.
function getTypes() {
MongoClient.connect(url, async function (err, db) {
let dbo = db.db("myDb");
return await dbo.collection("data").distinct("sample_type", {});
});
}
console.log(getTypes()); //Promise { undefined }
I don't think you are going to be able to achieve what you are looking for. Async await only works once you are in scope of an async function. Your top level calls are not inside an async function so you are forced to handle the returned Promise or callback.
e.g. getAllSampleTypes().then(function(response){});
Here are a couple of samples that are similar to what you want, but either way, the top level call into an async function will have to handle the response as a Promise.
const url = "mongodb://localhost:27017/";
getAllSampleTypes().then(function(sample_types){
// Do something here.
});
async function getAllSampleTypes() {
var db = await mongo.connect(url);
var dbo = db.db("myDb");
return await dbo.collection("data").distinct("sample_type", {});
}
It's important to understand that async await really isn't anything magical, behind the scenes it's translated to Promises really. That's why your top level call into an async function can handle the response with a .then(). It's just really much cleaner to read. The code above would roughly get translated and executed as:
const url = "mongodb://localhost:27017/";
getAllSampleTypes().then(function(sample_types){
// Do something here.
});
function getAllSampleTypes() {
return new Promise(function(resolve, reject){
mongo.connect(url).then(function(db){
var dbo = db.db("myDb");
dbo.collection("data").distinct("sample_type", {}).then(function(results) {
resolve(results);
});
});
});
}
getTypes doesn't return anything. You've gotta pass it up
If you're gonna use async/await try something like
async function getTypes() {
const db = MongoClient.connect(url);
const dbo = db.db("myDb");
return await dbo.collection("data").distinct("sample_type", {});
}
console.log(await getTypes());
These might be helpful:
How can I use asyn-await with mongoclient and how-to-use-mongodb-with-promises-in-node-js
Also, you should probably close the connection with db.close() somewhere

Where is future necessary to run a sequence of async functions in node?

Previously, I have used future.wait() to wait for a value before I return it to the next function. (I am coding in Meteor by the way)
After reading node's non-blocking I/O architecture, doesn't my following method (which does work) defeat the purpose entirely?
What I am doing is passing the returned result from the following 'requesting' function into another function. Instead, is the callback method the best convention?
The only reason why I used future was because using the return gets executed immediately. I want to learn the best practice as I think I am adding extra layers of unnecessary code using future.
If I use the callback convention, callback(null, result), does 'callback' wait for objects to be passed into the arguments?
My code using future to wait for results:
function requesting(perm_data) {
var f = new future();
request.get('example.com/api/auth_user', {
oauth: {consumer_key:'somekey',
token: perm_data.oauth_token,
token_secret: perm_data.oauth_token_secret
}
}, function response(error, response, body) {
if (!error && response.statusCode === 200) {
var bodyToJson = parser.toJson(body, options)
var userId = bodyToJson.user.id
return f.return(userId)
}
else {
f.throw(error)
}
})
return f.wait()
}
function nextFunction(data) {
//do some thing with data
}
Meteor.methods({
sequenceAsyncFunctions: function() {
try {
var resultOne = requesting()
var resultTwo = nextFuntion(resultOne)
} catch (e) {
//handling my errors
}
}
})
'callback' wait for objects to be passed into the arguments
in fact the callback does not wait for the object. The callback is called with the argument by the function you give it has argument.
You could use one of the promise library such as Q to handle this
Here is some article describing it in more depth
https://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/
Nodeschool tutorial: https://github.com/stevekane/promise-it-wont-hurt
Some alternatives are Kew and Async.
Promises have been specified, you can see more details and library here: http://wiki.commonjs.org/wiki/Promises/A
I noticed you tagged this question meteor. Meteor uses Fibers on the server. This package gives you co-operative multitasking using a sort of lightweight thread (called a fiber). This allows you to write "blocking" functions like your requesting without actually blocking the event loop. Future lets you use callback-based node.js functions in a blocking way using Fibers.
Without Fibers, it is impossible to write a function like your requesting which does I/O and returns the result directly. Instead you'd have to pass a callback (or use promises).
Idiomatic Meteor code should make use of Fibers on the server to make "blocking" functions. This is how the core Meteor APIs work, and is necessary inside Meteor methods. Rather than using future, you might find Meteor.wrapAsync more convenient.
In regular node.js code you could use Fibers, but it's more common to use function (err, result) {...} callbacks or promises. In the browser (including Meteor client code) you can't use Fibers, so you have to use callbacks or promises.
Here's how you would sequence operations using future, regular callbacks and promises:
// Future
function doTwoThings() {
var future = new Future();
asyncFunction(argument, function (err, result) {
if (err) future.throw(err);
else future.return(result);
});
var result = future.wait();
var future2 = new Future();
anotherAsyncFunction(result, function (err, result2) {
if (err) future2.throw(err);
else future2.return(result2);
});
var result2 = future2.wait();
return result2 + 2;
}
// wrapAsync
var wrappedAsyncFunction = Meteor.wrapAsync(asyncFunction);
var wrappedAnotherAsyncFunction = Meteor.wrapAsync(anotherAsyncFunction);
function doTwoThings() {
var result = wrappedAsyncFunction(argument);
var result2 = wrappedAnotherAsyncFunction(anotherAsyncFunction);
return result2 + 2;
}
// Regular callbacks
function doTwoThings(callback) {
asyncFunction(argument, function (err, result) {
if (err) return callback(err);
anotherAsyncFunction(result, function (err, result2) {
if (err) return callback(err);
callback(null, result2 + 2);
});
});
}
// Promises
function doTwoThings() {
return asyncFunctionReturningPromise(argument).then(function (result) {
return anotherAsyncFunctionReturningPromise(result);
}).then(function (result2) {
return result2 + 2;
});
}

Categories

Resources