Connecting a different user using mongoClient connexion pool - node.js

I 'm using a MongoClient connexion pool at the top of my app.js node.js back end, it works well:
var db;
MongoClient.connect("mongodb://localhost:27017", function(err, client) {
if (err) throw err;
db = client.db('myDb');
// Start the application after the database connection is ready
app.listen(3000);
console.log("Listening on port 3000");
});
app.post('/getEvaluations', function(req, res) {
db.collection('evaluations').find().toArray(function(err, docs) {
if (err) throw err;
res.send(docs);
});
})
I want to build a html form, for getting the login password, but how can I change the MongoClient connexion with the new login password afterwards ?
For example, as long as John logs in, he needs to have READ rights to be able to call the getEvaluations web service.
Do I have to always make the connexion with no user , then only check the user session inside each of the web services ?
Or Is it possible to change the pool connexion , each time a mongoDb user logs in ( he might only have READ rights)?
Something like this ?
MongoClient.connect("mongodb://john:passwd#localhost:27017", function(err, client) {
if (err) throw err;
db = client.db('myDb');
// Start the application after the database connection is ready
app.listen(3000);
console.log("Listening on port 3000");
});
For example, let's say "John" with the password "passwd" wants to log in, how could I do ? Is there any standard code somewhere using a connexion pool ?
I also use express-session but dont understand how to use it with the connexion pool.
I can't find a mongoDb auth code using the connexion pool. There are some moogoose examples but, it is not the same .
This is the idea I've got :
The user logs in via HTTP, then if the login is ok, so a session is created, but how could i check afterwards if the user only has READ rights during all of his session ?
This is a pseudo code, from a moongoose example, but this is not what i need, i'm using mongoClient.
app.get('/getAuth', function(req, res) {
var email = req.param('email');
var password = req.param('password');
//authenticate input against database
UserSchema.statics.authenticate = function (email, password, callback) {
User.findOne({ email: email })
.exec(function (err, user) {
if (err) {
return callback(err)
} else if (!user) {
var err = new Error('User not found.');
err.status = 401;
return callback(err);
}
bcrypt.compare(password, user.password, function (err, result) {
if (result === true) {
return callback(null, user);
} else {
return callback();
}
})
});
}
})
If somebody has an example code, i would really appreciate it, I don't use moogoose , but mongoClient Thank you.
or maybe, this auth web service would be better :
app.get('/getAuth', function(req, res) {
var user = req.param('user');
var password = req.param('password');
db.authenticate(user, password, function(err, res) {
// callback
});
})
but then, what should I do with the connexion pool ? How could I reload it ?
I can't understand how to do.
EDIT 1: This is what I got for now : I've built a getAuth web service, with a pool connexion starting inside of it, is it the right way to do ?
var db;
app.get('/getAuth', function(req, res) {
var user = "jose";
var password = "passwd";
MongoClient.connect("mongodb://"+user+":"+password+"#localhost:27017", function(err, client) {
if (err) {console.log(err)}
else{
db = client.db('myDb');
// Start the application after the database connection is ready
console.log("correct log in");
}
});
})
how could I use a session now?
EDIT 2 : Seems to be working code with express-session using this doc :
https://codeforgeek.com/manage-session-using-node-js-express-4/
//use sessions for tracking logins
app.use(session({secret: 'ssshhhhh',saveUninitialized: true,resave: true}));
sess ={};
// ---------------------------------------------------------------------AUTH USER
app.get('/getAuth', function(req, res) {
var user = "jose";
var password = "passwd";
MongoClient.connect("mongodb://"+user+":"+password+"#localhost:27017", function(err, client) {
if (err) {console.log(err)}
else{
db = client.db('myDb');
sess = req.session;
/*
* Here we have assign the 'session' to 'sess'.
* Now we can create any number of session variable we want.
* in PHP we do as $_SESSION['var name'].
* Here we do like this.
*/
sess.email = "jose.golo#gmail.com"; // equivalent to $_SESSION['email'] in PHP.
sess.username = "jose"; // equivalent to $_SESSION['username'] in PHP.
res.end('done');
}
});
})
Then, in my getEvaluations web service, i 'm checking if sess contains an email, otherwize, it means that he's not logged in, so he can't access the evaluations node.js web service :
app.post('/getEvaluations', function(req, res) {
if (!sess.hasOwnProperty("email") ){
console.log('not logged in ');
}else{
db.collection('evaluations').find().toArray(function(err, docs) {
if (err) throw err;
res.send(docs);
});
}
})
If you have a better solution, please let me know . This app is front end Ionic 1 app .
Question :
Is it a normal behavior :
I get authenticated inside of firefox, so i can access my evaluations.
Now, I go to chrome, on the same computer , i don't have to authenticate to view the evaluations ?
Is it because it is a user Id based session ?

This is how I would do it:
Let the user log in via a HTTP form using the POST method.
Try and find the user's document inside your mongo database (eg. by username). This will result in a user object containing your user's credentials and whatever rights he/she has.
Compare the credentials with the input provided by the log-in form (which is contained within the request object's body).
If the given input and previously stored credentials are equal to each other you can create a session and assign the user object to it.
Check if there's a session object inside the request when the user navigates to '/getEvaluations'. This session object will contain user object of the currently logged in user.
Respond with your Evaluations object if and only if the user has the appropriate
rights.
You could try out authentication middleware like Passport.js. These will handle the sessions and its user data object for you. Here's an example using the local strategy: https://github.com/passport/express-4.x-local-example

Related

Preventing shared session with usergrid authentication

I have a node site using Usergrid as the backend. I have created a login form screen, however when one user logs in it then shows that user being logged in to all other users who are on the site. If someone else logs in then it will overwrite the previously logged in user. How can I prevent the authenticated session from being shared across all users? I want each user to have their own authenticated session while browsing the site.
Login Code:
app.post("/login", function(req, res) {
if (client.isLoggedIn()) {
console.log("already logged in");
res.send({"status": "success"});
} else {
client.login(req.body.username, req.body.password, function(err) {
logger.debug("After Log In");
if (err) {
logger.error('Login Failed');
logger.error(err);
} else {
logger.debug(client.token);
client.authType = Usergrid.AUTH_APP_USER;
var options = {
method: 'GET',
endpoint: 'users/me'
};
client.request(options, function(err,data) {
if (err) {
console.log(err);
} else {
req.session['current_user'] = data.entities[0];
console.log(data);
console.log("SESSION");
console.log(req.session);
}
res.send({"status": "success"});
});
}
});
}
});
I think the problem is that you are using one instance of the Usergrid.Client object to serve many users. Instead, you should do what Usergrid does: when a user logs in, you give them the Usergrid access_token. You could send it back in a cookie, or in JSON data or whatever you choose.
Then you would expect subsequent HTTP request from the user to include the access_token in the URL or in a cookie, or whatever. On each request you create a new instance of the Usergrid.Client and pass in the token from the user, e.g.
var client = new Usergrid.Client({'token':'abcd5764adf...');

express-session: Why is redirect executed before my session info is set?

Yet another "I've just started learning node.js/express and now I stuck" person here. I've tried looking everywhere for answers but I've reached a dead end. This is my first Stackoverflow question, so please tell me if I have done something wrong or unconventional.
I'm trying to create a POST request that saves a User-object to an express-session (on MongoDB), and redirects you to a URL that handles your session information.
The problem is, that the user is redirected before the header is set, and I am given the following:
Error: Can't set headers after they are sent.
Here is my code. I know it's a lot.. Im sorry.
router.post()
// Handler for POST requests from root page (/)
router.post('/', function(req, res) {
console.log("Router: A POST request for: \"/\" has been called!");
var username = req.body.username;
var password = req.body.password;
// Connect to database
mongoose.connect(dbAddress);
var db = mongoose.connection;
// Report if error in connection
db.on('error', console.error.bind(console, 'Database: Connection error..'));
// Callback function on 'open'
db.once('open', function() {
console.log("Database: Connection successful!");
console.log("Database: User supplied username: " + username);
console.log("Database: User supplied password: " + password);
// Find User object with a userId of req's username
User.findOne({ 'userId' : username.toUpperCase() }, function(err, userObj) {
if (err)
return console.err(err);
// Disconnect when done retrieving user object
mongoose.disconnect();
if ( userObj ) {
console.log("Database: Returned password from MongoDB:");
console.log(userObj.password);
var db_password = userObj.password;
if (password === db_password) {
console.log("Database: User Authenticated");
// Set 'req.session.user' as cookie
req.session.user = userObj;
// Redirect to homepage, with new cookie set
res.redirect('/');
} else { // If passwords don't match
res.render('index', {
showError: true
});
}
} else { // If userObj is null
res.render('index', {
showError: true
});
}
});
});
});
Note the 'req.session.user = userObj' part. Here I am trying to set 'user' in the session to the user object retrieved from MongoDB. In the next line, I am redirecting the user back to the GET request handler for '/' which handles the user based on the session information.
For some reason, these aren't happening in order. The GET request handler doesn't find req.session.user.
I understand that node.js is all about asynchronisation, but from other examples I've found online, this is supposed to work. What am I missing here?
You could put your redirect inside a callback after the session is saved e.g:
...
// Set 'req.session.user' as cookie
req.session.user = userObj;
req.session.save(function(err) {
// session saved
res.redirect('/')
})
...
Hopefully this will make sure that the user is only redirected after the session is saved.
Note: Make sure you hash your password with something like Bcrypt or pbkdf2.

Sails.js : How to return access token after user registration using sails-generate-auth?

I am using sails-generate-auth in my sails.js app. I followed this tutorial to integrate this with my app. When I call localhost:1337/auth/local/register it routes to my callback action in AuthController. My callback action is as follows
callback: function (req, res) {
function tryAgain(err) {
//some validation
}
}
passport.callback(req, res, function (err, user, challenges, statuses) {
if (err || !user) {
return tryAgain(challenges);
}
req.login(user, function (err) {
if (err) {
return tryAgain(err);
}
//Return the access token created by passport instead of success.
res.send("Success");
});
});
I want to replace res.send("Success"); with the access token created by passport. But User.passport seems to be null at this point. How do I get the user's access token at this point?
The User You Get only contains the data from the 'User Collection' which contains the username email and id. The 'Passport' collection is a seperate collection which contains hashed password, ID, userID(which is equal to the ID In the 'User' Collection) and a token. You need to search in the 'passport' collection for the relavent User. Here is the algoritem:
passport.callback(req, res, function (err, user, challenges, statuses) {
if (err || !user) {
console.log(err);
return tryAgain(challenges);
}
req.login(user, function (err) {
if (err) {
console.log(err);
return tryAgain(err);
}
// Mark the session as authenticated to work with default Sails sessionAuth.js policy
req.session.authenticated = true
console.log(user);
var userID = user.id;
Passport.find({user: userID}, function(err, items){
if(err) return err;
console.log(items[0].accessToken);
// Make sure you dont give them any sensetive data
res.json({userData: user, token: items[0].accessToken});
});
// Upon successful login, send the user to the homepage were req.user
//res.redirect('/');
});
});
If you want to use sails built-in sessions, you do not need to send any token to the client, everything is stored server-side in the sessions and it is tied to the user by the sid (session id) cookie, therefore you can redirect to any page.
As long as you have your sessionAuth policy it will check that the user is logged-in before accessing your protected routes.
If you would like to use something like Json Web Tokens (JWT) though, sails-generate-auth / sails-auth do not support it yet

Access to "req" Object in Supertest After a Response

Is there any way to directly access the req object in supertest, while/after the request is being tested? I want to test my passport strategies, so I want to check req.user, req.session, and perhaps others. I know I can test page redirects or flash, as those are what my strategies do, but it seems useful to see if there is a user on the req object, as well. If I do this, I can also check how many users there are at any one time.
I will sign users up with the "local-signup" strategy, which is defined thusly:
'use strict';
// get passport & mongoose
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var UserModel = require('mongoose').model('User');
module.exports = function() {
// signup function
passport.use('local-signup', new LocalStrategy({
passReqToCallback: true // pass the entire request to the callback
},
function(req, username, password, done) {
process.nextTick(function() {
// find a user with the same username
UserModel.findOne({username: username}, function(err, user) {
// if there is an error, log it then return it
if(err) {
console.log("Error finding a user in the database: " + err);
return done(err);
}
// if a user was already found
if(user) {
return done(null, false, "User already exists");
}
// if we get this far, create a new user from the request body
var newUser = new UserModel(req.body);
// save it and sign it in
newUser.save(function(err) {
if(err) {
console.log("Error during signup: " + err);
return done(err);
}
return done(null, newUser);
});
});
});
}
));
};
One way I use this strategy is like this:
My "local" strategy is defined like this:
'use strict';
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var UserModel = require('mongoose').model('User');
module.exports = function() {
// create our local passport strategy & use it
passport.use(new LocalStrategy({
// use the default names for the username & password fields
usernameField: 'username',
passwordField: 'password'
},
// main strategy function
function(username, password, done) {
// find user with given username
UserModel.findOne({
username: username
},
// with this username, do this
function(err, user) {
// if there's an error, log it then pass it along
if(err) {
console.log("Error during login: " + err);
return done(err);
}
// if the username and/or password is incorrect, return an error
// along with a message
if(!user || !user.authenticate(password)) {
return done(null, false, {
message: 'Invalid username and/or password'
});
}
// if everything is correct, return the user document from the database
return done(null, user);
});
}
));
};
I use both strategies like this, for example:
app.route(pageName).post(function(req, res, next) {
passport.authenticate(strategyName, function(err, user, info) {
if(err || !user) {
res.status(401).send(info);
}
else {
req.login(user, function(err) {
if(err) {
res.status(400).send(err);
}
else {
res.send(null);
}
});
}
})(req, res, next);
});
I tried
request = require('supertest');
this.authServer = require('../my-server');
request(this.authServer)
.put('/signup')
.set('Content-Type', 'application/json')
.set('Host', 'konneka.org')
.send(this.fullUser)
.end(function(req, res, done) {
console.log(res);
});
The res object I logged, inside the end() function, which was way too long to show here, has a req object defined on it, but it seems to only have the objects & functions that were defined before the request was opened. In other words, it does not have req.user, req.session, or other objects I want, because they are defined after the request completes and a new request is started. I noticed it has status codes, as well, which are only defined after the request completes, so I must be missing something.
Is there any way to get access to the req object after the request you are testing is ended? Or am I going about this completely the wrong way?
You cannot do what you want using supertest.
Not sure if this helps but I'll add a little context to clarify the answer:
supertest is a wrapper on top of superagent (client side) with some basic hooks into express to start up the HTTP listener. Under the hood, it really is not any different from starting up your express app, waiting for it to listen on a port, making an HTTP request to that port, and parsing the result. In fact, that is exactly what it does.
So essentially supertest only has access to what ever your client would have access to (a browser or some API client). In other words, if it isnt in the HTTP response body, you wont have access to it. req.user and req.sesssion are server side state variables that are (most likely) not in the response (unless you are doing something strange).
If you want to test in exactly the way you describe, you will have to use some alternative strategy of testing, not supertest.
I found this question when I thought I wanted to do this, and for me it worked well to check the status of the user created by the request instead of verifying the content of the req object. You do have access to the full database where I assume you users somehow ends up.

sails session writing bug

I'm using sails 0.10.4 and stumbled with one pretty annoying bug. When user logs in I write his data into the req.session.user then in policies I can retrieve his data such as his role, password etc. But the req.session.user becomes undefined when I go out of the login action. Do you have any ideas how to handle this? Here's the code:
api/controllers/User.js :
module.exports = {
login: function (req, res) {
Users.findOneByEmail(req.param('email'))
.exec(function (err, user) {
if ((err) || (!user)) {
res.send({
error: 'User not found'
});
return;
}
if (!passwordHash.verify(req.param('password'), user.password)) {
res.send({
error: 'Incorrect passwpord'
});
return;
}
req.session.user = user;//I write user into the session
res.send({
user: user
});
});
}
}
api/policies/isLoggedIn.js
module.exports = function (req, res, next) {
if (req.headers.authentication) {
var credentials = JSON.parse(req.headers.authentication);
if(req.session.user.login === credentials.login)//User doesn't exist in session
return next();
}
}
In a testing environment , this issue can happen when testing with Supertest and not defining an agent
var agent = request.agent(app);
agent.post('/api/login',{email:'foo#bar.com',password:'foobar})
.end(function(err,res){...; done();});
It is the correct way to work with sessions, simply using request.post would not work as it would reinit the session variable as soon as the response is sent, even if we are chaining requests inside the same test.
Learnt it the hard way, so I hope it can help some lost developper.

Resources