Expressjs - How to prevent a user from posting a registration form...? - node.js

Is the following an appropriate way to redirect a user if they are trying to POST data to the registration form if they are already logged in or is there a better more secure way? I am preventing them from a GET request to the registration form, but I also want to prevent them from making a POST request thought other means then a form.
The check is:
if(true !== req.session.loggedIn){
}else{
}
In the app.js file:
app.get('/register',register.registration);
app.post('/register',register.doRegistration);
This is the controller file:
var mongoose = require('mongoose');
var User = mongoose.model('User');
exports.registration = function(req,res){
//Redirect the user if they are logged in
if(true !== req.session.loggedIn){
res.render('register', {
});
}else{
res.redirect('/user');
}
}
exports.doRegistration = function(req,res){
//Redirect the user if they are logged in
if(true !== req.session.loggedIn){
User.create({
fullName: req.body.register.fullName,
email: req.body.register.email,
password: req.body.register.password,
createdOn: Date.now()
}, function(err, user){
if(err){
console.log(err);
res.redirect('/register');
}else{
console.log('success');
req.session.user = {
"name": user.fullName,
"email": user.email,
"_id": user._id
};
req.session.loggedIn = true;
res.redirect('/user');
}
});
}else{
res.redirect('/user');
}
}

Firstly, instead of repeating yourself for each route you could simply use some middleware to check if a user is logged in or not and add any logic there:
function ensureNewUser(req, res, next) {
if (req.session.loggedIn) {
// if user is logged in redirect to /user
res.redirect('/user');
} else {
// else next
next();
}
}
Then use that middleware in your route definitions:
app.get('/register', ensureNewUser, register.registration);
app.post('/register',ensureNewUser, register.doRegistration);
Not completely sure what you mean by "more secure way", hard to comment without knowing how you are handling authentication but by "prevent them from making a POST request thought other means then a form" I assume you are talking about CSRF. In which case you should ensure you are using some CSRF middleware app.use(express.csrf()); or if using Express4 something like the csurf module and then ensuring you are sending back the token in the request from your front end.

Related

Can I use res.redirect and res.send simultaneously in Node.js?

I want to print user's info on the landing page after they get logged in. I am using res.redirect to redirect to landing page i.e index1.html after a user get successfully logged in.
Now I want to get user info. Here is my node.js code:
app.post('/login-feedback', function (req, res) {
dbConn.then(function(db) {
delete req.body._id; // for safety reasons
var dd = db.db("customermanagement");
dd.collection('admin').findOne({username: req.body.username, password: req.body.password}, function(err, user){
if(err) {
console.log(err);
}
else if(user){
req.session.user = user;
console.log( req.session.user.username);
res.status(200);
res.redirect('/index1.html');
}
else {
res.redirect('/login1.html');
}
});
});
});
I would love if you will tell me explicitly everything step by step.
If you want to send data to a static file, like an html file, you should use res.render :
res.render('/login1.html', {user: req.session.user.username})

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...');

Return value from one function to another with Node.JS

I am working on a login interface using the MEAN stack. I have managed to get it to work using PassportJS. My problem now is I need a way to let my client-side know whether the person logging in is an admin or user(user role). These info are available from my MongoDB.
The flow of my API call is as follow :
app.post('/login', passport.authenticate('local'), authRoutes.loginCheck);
First, it runs the passport.authenticate where it calls the function below
function verifyCredentials(username, password, done) // username & password from what user provide when logging in
{
console.log('VC');
User.findOne({username: username}, function(err, user) //query Mongo
{
console.log(user); // User role is available here, in JSON format
if(user === null) // if no username in database, do this
{
console.log('Username does not exist in database');
}
else
{
user.comparePassword(password, function(err, match) // function written to compare hashed password in Mongo & password provided by user
{
if(match)
{
done(null, {id: username, name: username});
return user; // this is not the correct syntax, but the idea is, I want to send over the user details here, so I can access the role later
}
else
{
done(null, null);
}
});
}
});
}
The verifyFunction is called with this syntax.
passport.use(new LocalStrategy(verifyCredentials));
Once that function is successfully called, the server executes the 2nd part of it which is the loginCheck.
module.exports.loginCheck = function(req, res)
{
console.log('Calling loginCheck route');
// I generate some sort of jwt token here
// payload, body, blah blah blah ...
console.log(req.body);
res.json({
authenticated: req.isAuthenticated(), //built-in authentication function, returns true or false
token: token // sends over token
role: user.role // want to send over something like this
}); // sends all these to client side as JSON
}
Since both functions are in different files, I am unclear if I have to require something or simply just pass an extra parameter to the loginCheck function. I have tried the latter though and it did not work.
One way that I could think of is do another Mongo query in the loginCheck function, but that would be kinda redundant.
Even a specific keyword for me to google up would definitely be of big help as don't I know what I should be looking for. The reason is because I am new to NodeJS, thus I am not familiarize with most of the terms yet.
I think these codes should suffice but if I am needed to provide more, let me know and I will do so. Thanks in advance !!
To pass control to next matching route you need to use next that passes as third argument in the routes:
function verifyCredentials(req, res, next) {
User.findOne({username: req.body.username}, function(err, user) //query Mongo
{
if(user === null) {
return next(new Error('Username does not exist in database'));
} else {
user.comparePassword(req.body.password, function(err, match) {
if(match) {
next(null, {id: username, name: username});
} else {
next(new Error('not match'));
}
});
}
});
}
app.post('/login', verifyCredentials, authRoutes.loginCheck);

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