I'm new to mongoose and node.js. I try to follow this tutorial: https://scotch.io/tutorials/using-mongoosejs-in-node-js-and-mongodb-applications#sample-model-for-users
In my entry point index.js, I tried to call "chenya.saltHashPassword(function(err, passwordHash)". It is actually called in user.js because user.js can print out three log messages; however, there is no log message for this method call at all in index.js. In contrast, the save method can print out log message indicating successful saving.:
//Lets load the mongoose module in our program
var mongoose = require('mongoose');
//Lets connect to our database using the DB server URL.
mongoose.connect('mongodb://localhost:27017/server_naturis');
// if our user.js file is at app/models/user.js
var User = require('./user');
// create a new user called Chenya
var chenya = new User({
userName: 'Chenya',
email: 'chenya#gmail.com',
password: 'Chenya'
});
// call the custom method. hash the password
chenya.saltHashPassword(function(err, passwordHash) { // NOT CALLED!
if (err) {
console.log('chenya.saltHashPassword: ' + err);
} else {
this.password = passwordHash;
console.log('Your hashed password is ' + passwordHash);
}
});
// call the built-in save method to save to the database
chenya.save(function(err) { // CALLED!
if (err) {
console.log('chenya.save: ' + err);
} else {
console.log('User saved successfully!');
}
});
In my user.js, I have the schema function "userSchema.methods.saltHashPassword":
// grab the things we need
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// Require the crypto module for password hash
'use strict';
var crypto = require('crypto');
// create a schema
var userSchema = new Schema({
userName: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
});
// add a schema method
/**
* generates random string of characters i.e salt
* #function
* #param {number} length - Length of the random string.
*/
var genRandomString = function(length){
return crypto.randomBytes(Math.ceil(length/2))
.toString('hex') /** convert to hexadecimal format */
.slice(0,length); /** return required number of characters */
};
/**
* hash password with sha512.
* #function
* #param {string} password - List of required fields.
* #param {string} salt - Data to be validated.
*/
var sha512 = function(password, salt){
var hash = crypto.createHmac('sha512', salt); /** Hashing algorithm sha512 */
hash.update(password);
var value = hash.digest('hex');
return {
salt:salt,
passwordHash:value
};
};
/**
* a function that will use the above function
* to generate the hash that should be stored
* in the database as user’s password.
*/
userSchema.methods.saltHashPassword = function() {
var salt = genRandomString(16); /** Gives us salt of length 16 */
var passwordData = sha512(this.password, salt);
console.log('UserPassword = '+ this.password);
console.log('Passwordhash = '+ passwordData.passwordHash);
console.log('\nSalt = '+ passwordData.salt);
return passwordData.passwordHash;
}
// the schema is useless so far
// we need to create a model using it
var User = mongoose.model('User', userSchema);
// make this available to our users in our Node applications
module.exports = User;
Terminal:
UserPassword = Chenya
Passwordhash = 5bb5bf2181e2c713bae1eb49d1f3646b23db839368d38c33951774c92cec39a3c4b855aea30875e72cce6f271bdbdb27de8976c9316df09d086435b6c5629548
Salt = a88384d072b720de
(node:11717) DeprecationWarning: Mongoose: mpromise (mongoose's default promise library) is deprecated, plug in your own promise library instead: http://mongoosejs.com/docs/promises.html
User saved successfully!
You're not passing in a callback parameter into userSchema.methods.saltHashPassword but treating the function as if you did.
Change userSchema.methods.saltHashPassword to:
userSchema.methods.saltHashPassword = function(callback) { // <-- Add callback param
var salt = genRandomString(16); /** Gives us salt of length 16 */
var passwordData = sha512(this.password, salt);
console.log('UserPassword = '+ this.password);
console.log('Passwordhash = '+ passwordData.passwordHash);
console.log('\nSalt = '+ passwordData.salt);
// Your function that you passed in is called here
callback(null, passwordData.passwordHash);
}
The reason that you're callback was not called in saltHashPassword but was called in save is because Mongoose defines that method to require a callback function that takes in a parameter for an error and the actual return value.
When an error occurs, it is expected that the callback will have error handling defined, which is good practice and why you see tutorials suggesting you do the same. When you define your own method for a Schema you no longer have that and have to set it up yourself.
So as you can see in the function above, that's what happened. Now you're callback is passed in as a parameter calling it with callback(null, passwordData.passwordHash) will make it execute. If you ever had an error occur, you could then save it as variable, e.g. err and pass that into your function as callback(err, null)
Going back to salts. I haven't read your tutorial but it's important that you save them in the database along with your user data so that passwords can be verified when a user logs in.
Good resource here:
Password Hashing add salt + pepper or is salt enough?
You need the salt to generate the same hashed password you store in the database. If you don't have access to that salt, you can't know if the password you're given would generate the same hash.
Related
I am creating an app using express. And I have a SOAP API request. In this SOAP API, I have to send nonce, timestamp and digest password. First of all I tried this with PHP and I sent the request successfully and got the response. Now I would like to do this with also Node Js. Then I tried wsse npm package. But, this didn't create the correct password. Here is what I tried.
const wsse = require('wsse');
const token2 = new wsse.UsernameToken({
username: 'hdfhrhe', // (required)
password: 'ergerherh', // (required)
created: Timestamp, // (optional) you can specify `craeted`.
nonce: NonceWithEncode, // (optional) you can specify `nonce`.
sha1encoding: 'hex' // (optional) you can specify `sha1encoding` for wrong WSSE Username Token implementation.
});
console.log(token2.getWSSEHeader());
What I need to do this.
digest_pw = Base64 ( SHA-1 ( nonce + timestamp+ SHA-1 ( password ) ) );
How can I do this ?? Are there any method ??
First you must require crypto library:
const crypto = require('crypto');
Then, define some functions:
function someId() {
// function taken from https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid
// creates a random 20 characters hex string
return 'xxxxxxxxxxxxxxxxxxxx'.replace(/x/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function md5(str, encoding) {
return crypto.createHash('md5').update(str).digest(encoding);
}
function passwordDigest(created, nonce, pass) {
// Password_Digest = Base64 ( SHA-1 ( bytes(decode64(nonce)) + bytes(created) + bytes(password) ) )
let pd = Int8Array.from([...Int8Array.from(Buffer.from(nonce, 'base64')),
...Int8Array.from(Buffer.from(created)),
...Int8Array.from(Buffer.from(pass))]);
pd = crypto.createHash('sha1').update(pd).digest('base64');
return pd;
}
// for example
// console.log(passwordDigest('2006-07-26T15:16:00.925Z', 'lckJBnhGHAj4EGG3YuGXmg==', '1111'));
// must print 'LiP3J84wKHpA6sMOu2BVVZRGYSY='
Now you can calculate variables to embed them in the header:
const usernametoken = `UsernameToken-${Math.round(Math.random()*10000000).toString()}`;
const username = 'myUserName';
const passwd = 'myPassword'; // this will not be sent
const created = (new Date).toISOString();
const nonce = md5(someId().substr(0,16), 'base64'); // Only 16 characters length before md5!
const passworddigest = passwordDigest(created, nonce, passwd);
Then you replace the variables you have calculated in a soap header:
const header = `<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="${usernametoken}" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>${username}</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">${passworddigest}</wsse:Password>
<wsse:Nonce>${nonce}</wsse:Nonce>
<wsu:Created>${created}</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>`;
So, finally, you must embed this header in your <soapenv:Envelope> before the <soapenv:Body>.
I thought I setup this Promise to attempt to perform a login POST request for a RESTful service using supertest. However, it is not working for me.
Here is the code:
'use strict';
/*
* loginRequest.js
*
* Perform a basic login using supplied values. Only tests for Response Code 200
*/
var base64_encode = require('Base64').btoa
, request = require('supertest')
, VerifyUrl = require('./VerifyUrl');
module.exports = loginRequest;
/**
* Perform a basic login and returns a Promise. It resolves with the response or rejects with the error.
*
* #param username {string} The username.
* #param password {string} The password.
* #param url {string} The url under test. If not supplied, assumes 'http://localhost:9000/'
* #returns {Promise} The Promise container of the login request which resolves with the POST response or rejects with the error.
*/
function loginRequest(username, password, url) {
return new Promise(function (resolve, reject) {
url = VerifyUrl(url); // Provides a default url if non is supplied, ensures a trailing '/'.
var authorization = "Basic " + base64_encode(username + ':' + password); // Yes, I know, not secure. The app isn't yet production ready.
console.log("creds = " + username + ':' + password);
console.log("auth = " + authorization);
request(url)
.post('api/users/login')
.set('authorization', authorization)
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200)
.expect(function() {console.log("Made the expectation.")})
.end(function (err, res) {
console.log("Reached the login .end method.");
if (err) {
reject(err);
} else {
resolve(res);
}
});
});
}
The output from this is:
creds = autoTest1465828536379:4ut0T3st
auth = Basic YXV0b1Rlc3QxNDY1ODI4NTM2Mzc5OjR1dDBUM3N0
When I run it through the debugger, with breakpoints on each request method, it stops at the .set, but does not run the .expect or .end methods. What am I missing or not understanding?
Additional info. From the spec script, I am using the above code as follows:
var login = require('loginRequest');
var username = 'autoTest' + Date.now();
var password = '4ut0T3st';
login(username, password, 'http://localhost:9000')
.then(function(loginResponse) {
// do stuff with the response
})
.catch(function(err) {
// do stuff with the err
});
But it never reaches the .then or .catch of this call. (The output is as described above.
/**
* Example of usage oDeskAPI
*
* #package oDeskAPI
* #since 09/22/2014
* #copyright Copyright 2014(c) oDesk.com
* #author Maksym Novozhylov <mnovozhilov#odesk.com>
* #license oDesk's API Terms of Use {#link https://developers.odesk.com/api-tos.html}
*/
var config = {
'consumerKey' : '571a5ff21bded617e499965f9cf013a0',
'consumerSecret' : 'e3df4989efed7761',
// 'accessToken' : 'xxxxxxxx', // assign if known
// 'accessSecret' : 'xxxxxxxx', // assign if known
'debug' : false
};
//var oDeskApi = require('../') // uncomment to use inside current package/sources
var oDeskApi = require('odesk-api') // use if package is installed via npm
// , Auth = require('../lib/routers/auth').Auth // uncomment to use inside current package/sources
, Auth = require('odesk-api/lib/routers/auth').Auth // use if package is installed via npm
, rl = require('readline');
// you can use your own client for OAuth routine, just identify it here
// and use as a second parameter for oDeskApi constructor (see the example of usage below)
// note: your client must support the following methods:
// 1. getAuthorizationUrl - gets request token/secret pair, creates and returns
// authorization url, based on received data
// 2. getAccessToken(requestToken, requestTokenSecret, verifier, callback) -
// requests access token/secret pair using known request token/secret pair and verifier
// 3. setAccessToken(token, secret, callback) - sets known access token/secret pair
// 4. get|post|put|delete(path, data, callback) - for GET, POST, PUT and DELETE methods respectively
// 5. setEntryPoint(entryPoint) - allows setup different entry point for base url
//
// var MyClient = require('../lib/myclient').MyClient;
//
// by default predefined lib/client.js will be used that works with other odesk oauth library
// a function to get access token/secret pair
function getAccessTokenSecretPair(api, callback) {
// get authorization url
api.getAuthorizationUrl('http://localhost/complete', function(error, url, requestToken, requestTokenSecret) {
if (error) throw new Error('can not get authorization url, error: ' + error);
debug(requestToken, 'got a request token');
debug(requestTokenSecret, 'got a request token secret');
// authorize application
var i = rl.createInterface(process.stdin, process.stdout);
i.question('Please, visit an url ' + url + ' and enter a verifier: ', function(verifier) {
i.close();
process.stdin.destroy();
debug(verifier, 'entered verifier is');
// get access token/secret pair
api.getAccessToken(requestToken, requestTokenSecret, verifier, function(error, accessToken, accessTokenSecret) {
if (error) throw new Error(error);
debug(accessToken, 'got an access token');
debug(accessTokenSecret, 'got an access token secret');
callback(accessToken, accessTokenSecret);
});
});
});
};
// get my data
function getUserData(api, callback) {
// make a call
var auth = new Auth(api);
auth.getUserInfo(function(error, data) {
// check error if needed and run your own error handler
callback(error, data);
});
}
(function main() {
// uncomment only if you want to use your own client
// make sure you know what you're doing
// var client = new MyClient(config);
// var api = new oDeskApi(null, client);
// use a predefined client for OAuth routine
var api = new oDeskApi(config);
if (!config.accessToken || !config.accessSecret) {
// run authorization in case we haven't done it yet
// and do not have an access token-secret pair
getAccessTokenSecretPair(api, function(accessToken, accessTokenSecret) {
debug(accessToken, 'current token is');
// store access token data in safe place!
// get my auth data
getUserData(api, function(error, data) {
debug(data, 'response');
console.log('Hello: ' + data.auth_user.first_name);
});
});
} else {
// setup access token/secret pair in case it is already known
api.setAccessToken(config.accessToken, config.accessSecret, function() {
// get my auth data
getUserData(api, function(error, data) {
debug(data, 'response');
// server_time
console.log('Hello: ' + data.auth_user.first_name);
});
});
}
})();
This is my example.js Currently Its giving the response url of odesk .Just will i try to access that url Error comes (You are not authorized to access this page.) I have installed the app successfully by using node.js .I have the main issue in configuration Right .
You have to provide the same call back Url Which you have provide Odesk While making app your Here
api.getAuthorizationUrl('http://localhost/complete', function(error, url, requestToken, requestTokenSecret) {
if (error) throw new Error('can not get authorization url, error: ' + error);
I would like to fill a couple of extra temporary properties with additional data and send back to the response
'use strict';
var mongoose = require('mongoose');
var express = require('express');
var app = express();
var TournamentSchema = new mongoose.Schema({
createdAt: { type: Date, default: Date.now },
deadlineAt: { type: Date }
});
var Tournament = mongoose.model('Tournament', TournamentSchema);
app.get('/', function(req, res) {
var tournament = new Tournament();
// Adding properties like this 'on-the-fly' doesnt seem to work
// How can I do this ?
tournament['friends'] = ['Friend1, Friend2'];
tournament.state = 'NOOB';
tournament.score = 5;
console.log(tournament);
res.send(tournament);
});
var server = app.listen(3000, function() {
console.log('Listening on port %d', server.address().port);
});
But the properties wont get added on the Tournament object and therefor not in the response.
Found the answer here: Unable to add properties to js object
I cant add properties on a Mongoose object, I have to convert it to plain JSON-object using the .toJSON() or .toObject() methods.
EDIT: And like #Zlatko mentions, you can also finalize your queries using the .lean() method.
mongooseModel.find().lean().exec()
... which also produces native js objects.
I'm writing a Node.js application using Express and a PostgreSQL database using node-postgres. I want to look up the current user's username and real name based on their email, and set them in req.session. However, if I set them where I am in the code below, they are undefined when we leave that block (i.e. the first console.log statements print the correct info, the second set prints undefined. How can I solve this?
var client = new pg.Client(app.conString);
var realname = "";
var username = "";
client.connect();
var query = client.query(
"SELECT * FROM users WHERE email = $1;",
[req.session.email]
);
query.on('row', function(row) {
req.session.realname = row.realname;
req.session.username = row.username;
console.log(req.session.realname);
console.log(req.session.username);
});
console.log(req.session.realname);
console.log(req.session.username);
query.on('end', function() {
client.end();
});
The second pair of console.log will execute before the query-results are available (in the row event handler).
If your code is going to be used in an Express route, you would use something like this:
app.get('/', function(req, res) {
var client = new pg.Client(app.conString);
var realname = "";
var username = "";
client.connect();
var query = client.query(
"SELECT * FROM users WHERE email = $1;",
[req.session.email]
);
query.on('row', function(row) {
req.session.realname = row.realname;
req.session.username = row.username;
});
query.on('end', function() {
client.end();
res.send(...); // <-- end the request by sending back a response
});
});
An alternative for using the EventEmitter interface for node-postgres would be to just pass a callback to query (which looks better with Express IMHO):
client.query(
"SELECT * FROM users WHERE email = $1;",
[req.session.email],
function(err, results) {
if (err)
// handle error
else
if (results.length)
{
req.session.realname = results[0].realname;
req.session.username = results[0].username;
}
res.send(...); // done
});