4

I have the following app.js code

app.configure(function(){
  app.set('port', process.env.PORT || 3000);
  app.set('views', __dirname + '/views');
  app.enable('jsonp callback');
  app.set('view engine', 'jade');
  app.set('view options', {layout : false});
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.cookieParser());
  app.use(express.session({
    secret : 'abcdefg'      
  }));
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
  app.use(function(req, res, next){
    res.locals.user = req.session.user;
    next();
  })

});

I'm trying to make it so that the following code on my .jade view will work

- if(session.user)
          div#logoutsection.pull-right
            a#logout-btn.btn.btn-info.pull-right.top-bar-form-button(href='logout/') Logout
            p#loginprompt.pull-right.login-prompt #{session.user.username} logged In
        - else
          ul.pull-right
            li
          a#signup-btn.btn.pull-right.top-bar-form-button(href='#signup-modal', data-toggle="modal") Sign Up

So if they are not signed in, provide the option to sign up, and if they are signed in, tell them they are 'logged in'. I added in the function at the end of the app.configure code as before it was using dynamicHelpers() I was told that cookieParser() was the way to go, but how would I code this in so that I could check whether my user was logged in and provide their username as I am trying to above?

Any help appreciated.

Thanks!

EDIT: index.js

'use strict'

var util = require('util');
var Logger = require('devnull');
var logger = new Logger({namespacing : 0});
var User  = require('../schemas/User');
var Post = require('../schemas/Post');

/**
  * Get Meta information about all the Post's
  */
var getAllMeta = function(req, res, next){
  Post.getAllMeta(function(err, postsList){
    if(!err && postsList){
      req.postsList = postsList;
    }
    next(err);
  });
};

/**
  * validate the signup credentials entered by the user
  * @param {String} username 
  * @param {String} pass1 : first password
  * @param {String} pass2 : verification password
  */
var validateUserData = function(username, pass1, pass2){
  if(pass1.trim() !== pass2.trim()){
    util.log('Passwords not Matching ' + pass1 + ' ' + pass2);
    return 'Passwords not Matching';
  }
  return '';
  //put any other validations here
};

/*
 * GET home page.
 */
module.exports = function(app){
  /**
    * Map the URL '/' to the callback
    */
  app.get('/', function(req, res){
    logger.log('Serving request for url [GET]' + req.route.path)
    Post.getAll(function(err, allPosts){
      if(!err && allPosts){
        res.render('index', {'allPosts' : allPosts});
      }else{
        util.log('Error fetching posts from database : ' + err);
        res.render('error');
      }
    });
  });

  /**
    * Map the URL '/login' to the callback
    */
  app.post('/login', function(req, res){
    logger.log('Serving request for url [POST] ' + req.route.path);
    var username = req.body.User;
    var password = req.body.Password;

    User.validateUser(username, password, function(err, user){
      if(err && !user){
        res.json({
          retStatus : 'failure'  
        });
      }else{
        console.log(user);
        req.session.user = user;
        res.json({
          retStatus : 'success',
          user : user ,
        });
      }
    });
  });

  /**
    * Logout the current user and clear the session
    */
  app.get('/logout', function(req, res){
    logger.log('Serving request for url [GET] ' + req.route.path);
    req.session.user = undefined;
    res.redirect('/');
  });

  /**
    * Add a new User to database
    */
  app.post('/signup', function(req, res){
    util.log('Serving request for url [POST] ' + req.route.path);
    var signupForm = req.body.signupForm;
    var username = signupForm.username;
    var pass1 = signupForm.pass1;
    var pass2 = signupForm.pass2;

    var validateMsg = validateUserData(username, pass1, pass2);
    if(validateMsg !== ''){
      res.json({
        'retStatus' : 'failure',
        'message' : validateMsg
      });
    }else{
      var newUser = new User();
      newUser.username = username;
      newUser.password = pass1;

      newUser.save(function(err, savedUser){
        var message = '';
        var retStatus = '';
        if(!err){
          util.log('Successfully created new user with Username : ' + username);
          message = 'Successfully created new user : ' + username;
          retStatus = 'success';
          req.session.user = savedUser;
        }else{
          util.log('Error while creating user : ' + username + ' error : ' + util.inspect(err));
          if(err.code === 11000){
            message = "User already exists";
          }
          retStatus = 'failure';
        }
        res.json({
          'retStatus' : retStatus,
          'message' : message
        });
      });
    }
  });

  app.get('/admin', getAllMeta, function(req, res){
    util.log('Serving request for url [GET] ' + req.route.path);    
    if(req.session.user){
      res.render('post', {'postsList' : req.postsList});
    }else{
      res.redirect('/');
    }
  });

  /**
    * Save the post to database
    */
  app.post('/admin/save/post', function(req, res){
    var postContent = req.body.postContent;

    if(postContent.postKey === '' || postContent.postKey === undefined){
      var post = new Post();
      post.subject  = postContent.postSubject;
      post.content  = postContent.postContent;
      post.author   = req.session.user.username;
      post.tags     = postContent.postTags;

      post.save(function(err, response){
        if(!err && response){
          util.log('Successfully saved Post with id : ' + response.id);
          res.json({
            'retStatus' : 'success',
            'data' : response
          })
        }else{
          util.log('Error saving the Post : ' + err);
          res.json({
          'retStatus' : 'failure',
            'error' : err
          });
        }
      });
    }else{
      var conditions = {'key' : postContent.postKey};
      var update = {
        'subject' : postContent.postSubject,
        'content' : postContent.postContent,
        'tags' : postContent.postTags
      };

      Post.update(conditions, update, null, function(err, numAffected){
        if(!err && numAffected){
          util.log('Successfully updated the Post with id : ' + postContent.postKey);
          res.json({
            'retStatus' : 'success',
            'numAffected' : numAffected
          });
        }else{
          util.log('Error updating the Post with id : ' + postContent.postKey + ' ' + err);
          res.json({
            'retStatus' : 'failure'
          });
        }
      });
    }
  });

  app.get('/post/show/:key', function(req, res){
    Post.findByKey(req.params.key, function(err, postData){
      if(!err && postData){
      postData = postData[0];
        res.json({
          'retStatus' : 'success',
          'postData' : postData
        });
      }else{
        util.log('Error in fetching Post by key : ' + req.params.key);
        res.json({
          'retStatuts' : 'failure',
          'msg' : 'Error in fetching Post by key ' + req.params.key
        });
      }
    }); 
  });

  app.post('/admin/save/', function(req, res){
    //container for saving a post
  });

  app.get('/admin/remove/:key', function(req, res){
    //container for deleting a post
  });

  app.get('/contact', function(req, res){
    util.log('Serving request for url[GET] ' + req.route.path);
    res.render('contact');
  });
};

User.js

'use strict'

var util    = require('util');
var bcrypt  = require('bcrypt');
var mongoose = require('mongoose');
var Schema   = mongoose.Schema;

var validatePresenceOf = function(value){
  return value && value.length; 
};

var toLower = function(string){
  return string.toLowerCase();
};

var User = new Schema({
  'username' : { type : String, 
              validate : [validatePresenceOf, 'a Username is required'],
              set : toLower,
              index : { unique : true }
              },
  'password' : String,
});

User.statics.findUser = function(username, password, cb){
  return  this.find({'username' : username}, cb);
};

User.statics.validateUser = function(username, password, cb){
  this.find({'username' : username}, function(err, response){
    var user = response[0];
    if(!user || response.length === 0){
      cb(new Error('AuthFailed : Username does not exist'));
    }else{
      if(password == user.password){
        util.log('Authenticated User ' + username);
        cb(null, user);
      }else{
        cb(new Error('AuthFailed : Invalid Password'));
      }
    }
  });
};

module.exports = mongoose.model('User' , User);
germainelol
  • 3,231
  • 15
  • 46
  • 82

2 Answers2

1

Maybe I'm understanding your question wrong, but the only thing you are missing is a route, where you authenticate the user, e.g.:

app.post('/login', function(req, res){
  if(req.body.user == 'Ryan' && req.body.password == 'Dahl'){
    req.session.user = aUserIdOrUserObject;
    res.send(200) // or redirect
  }
};          

This is hust pseudo code. You obviously want to check if user and password match against your database.

The second point you are missing is a permanent session store like https://github.com/masylum/connect-mongodb or https://github.com/CarnegieLearning/connect-mysql-session. The memory store is only usefull for development, in production this could kill your server.

Renato Gama
  • 16,431
  • 12
  • 58
  • 92
topek
  • 18,609
  • 3
  • 35
  • 43
  • Not sure I follow sorry, I'm kind of new to learning nodejs and all of its features. I'm connecting to my mongodb database like so: `mongoose.connect('mongodb://localhost/testdb');` But I'm not sure how to check against the database, or what to put where you have put `aUserIdOrUserObject`. Assuming all of this eventually works, how would you recommend checking this in the view? I read the links but they look like the matrix to me in some cases! – germainelol Nov 22 '12 at 20:39
  • I think your view code is fine. It's the login logig you are missing. If you are just trying to start you could use http://passportjs.org/ or another authentication package http://nodetoolbox.com/categories/Authentication . This should get you started easily. Another link that might help: http://stackoverflow.com/questions/3498005/user-authentication-libraries-for-node-js – topek Nov 22 '12 at 20:44
  • I keep reading about passport indeed. But my problem is taking the passport logic and applying it to my program which I have taken from here by the way and started editing `https://github.com/ric03uec/nodeblox/tree/tutorial_5` I seem lost. – germainelol Nov 22 '12 at 20:54
  • I think the only problem you are facing is that you are trying to achieve too much in one step. Maybe you should step back and just start with some basic login form (user, password), send it to a route like in my answer, check it against some predefined variables and set a session.user, if the credentials are correct. At this step you have a logged in user and can play around in the view. Later you can come back to your login logik and refine it. It's better to have something working now, than get stuck in other libraries code and give up. – topek Nov 22 '12 at 21:03
  • I thought the login logic was quite simple, if a user is logged in => print their username instead of login form. If not logged in => show login form. But my problem is simply what code to put in app.js in order for my view to know if I am logged in or not, I've searched everywhere and no help is really found, only examples where no one says where exactly to put the code :/ – germainelol Nov 22 '12 at 21:09
  • You do not need much more code than on this screen. With the post to the /login route you set a session variable. This session variable can be used on successive request to switch between logged in and logged out users in the view. The only part missing is a route where you display your view. `app.get('/, function(req, res){res.render('index', {user: req.local.user}) and in your view you can use the user variable. – topek Nov 22 '12 at 21:21
  • I have updated the original code with the /login post from `index.js` which is validating the user correctly? So the post is successfully set to the user with `req.session.user = user`. But the `app.get` part you posted. Where should I be putting this? – germainelol Nov 22 '12 at 21:39
  • ` app.get('/', function(req, res){ res.render('index', { user: req.local.user }) });` I have made sure this is on the index.js page, but this gives error `cannot read property user of undefined` Where or how should I be defining this? Sorry to pester you. – germainelol Nov 22 '12 at 21:52
  • this should be done in your middleware. Put a console log in your middleware and make sure it gets run. – topek Nov 22 '12 at 22:06
  • `http.createServer(app).listen(app.get('port'), function(){ console.log("Express server listening on port " + app.get('port')); app.get('/', function(req, res){ res.render('index', {user: req.local.user}) }); });` I should add it here? By middleware I understand this is where I'm making a connection of some sort to the server so I assume you mean here, where user is still not defined. I will try other places but I don't follow. – germainelol Nov 22 '12 at 22:14
  • middleware is where you set `res.locals.user = req.session.user; ` – topek Nov 22 '12 at 22:18
  • `app.use(function(req, res, next){ res.locals.user = req.session.user; next(); }) app.get('/', function(req, res){ res.render('index', {user: req.local.user}) });` Yep I now have this, but I still get the same error that I have not defined `user` :/ All your help is appreciated also I would like to add... – germainelol Nov 22 '12 at 22:21
  • put a `console.log("this should be called");`before `res.locals.user` and watch your console, if the function is really called. – topek Nov 22 '12 at 22:29
  • No I just see `Express server listening on port 3000` and if I try to go to localhost:3000 I get the same errors again. – germainelol Nov 22 '12 at 22:31
  • Not sure what to change after playing around with where to put different things – germainelol Nov 22 '12 at 22:52
  • Could you try to put that middleware before app.use(express.router). I'm not quite sure on this, but I think that I had once a similar problem. – topek Nov 22 '12 at 23:03
  • Hi, I have tried putting `app.use(app.router);` both at the end of `app.configure` and just after the `this should be called` function both giving the same error and not printing to the cmd window – germainelol Nov 23 '12 at 14:37
  • `app.get('/', function(req, res){ res.render('index', {user: req.local.user}) });` It is this `user:` that is not defined according to the errors – germainelol Nov 23 '12 at 16:54
1

What I do in the app I work, and in order to not have to do this validation in every controller action, is:

//userValidation.js
module.exports = function(req, res, next){
    if(req.body.user == 'Ryan' && req.body.password == 'Dahl'){
        next();
    }else res.send("Not auth");
}

//controller.js
var validate = require("./userValidation");

app.post("/route", validate, function(req, res){
    //if execution get to this point you are sure that user is authenticated.
});

This code is also from the App I work, this is how we set the session to work. For dev purposes you can replace MongoStore with a MemoryStore.

app.configure(function(){
        app.set('views', __dirname + '/views');
        app.set('view engine', 'jade');

        app.use(connect.compress());
        app.use(express.static(__dirname + "/public", { maxAge: 6000000 }));
        app.use(express.favicon(__dirname + "/public/img/favicon.ico", { maxAge: 6000000 }));    
        app.use(express.bodyParser());
        app.use(express.methodOverride());
        app.use(express.cookieParser());
        app.use(express.session({
            secret: config.sessionSecret,
            maxAge: new Date(Date.now() + (1000 * 60 * 15)),
            store: new MongoStore({ url: config.database.connectionString })
        }));
        app.use(function(req, res, next){
            console.log("\n~~~~~~~~~~~~~~~~~~~~~~~{   REQUEST   }~~~~~~~~~~~~~~~~~~~~~~~".cyan);
            res.locals.config = config;
            res.locals.session = req.session;
            res.locals.utils = viewUtils;
            next();
        });
        app.use(app.router);
        app.use(function(req, res, next){
            res.status(404).send("Resource not found");
        });
});

In order to set the user in the session we have this:

var User = require("../utils/modelRegistrar").user; //any way to get the User model
var userRepository = require("../domain/repositories/usuarioRepository");
var hash = require("../utils/hash");

module.exports.init = function(app, io){
    app.publicPost("/login", login);
    app.put("/exit", exit);
};

function login(req, res){
    var dadosDeLogin = req.body.dadosDeLogin; 
    userRepository.autenticar(dadosDeLogin.login, /*hash.md5(*/dadosDeLogin.senha/*)*/, function(err, user){
        if(err) req.next(err);
        if(user){
            user.lastAcess = new Date();
            user.access++;

            userRepository.update(user, ["lastAcess", "acess"], function(err){
                if(err) req.next(err);
                else{
                    req.session.logedUser = user;
                    res.redirect("/home");
                }
            });
        }
        else res.redirect("/#user-not-found");
    });
};

function exit(req, res){
    if(req.session.logedUser) delete req.session.logedUser;
    res.redirect("/");
}

May some part of the code is still in portuguese

Renato Gama
  • 16,431
  • 12
  • 58
  • 92
  • Thanks for reply, I have added the code I have for `User.js` under Schemas. This should work by itself? If you see the comments with the previous answer to the question, I am also trying to get the session working through his methods to no success yet – germainelol Nov 23 '12 at 15:22
  • Where/how are you defining `config.sessionSecret`, `config`, `memoryStore` and `viewUtils`? – germainelol Nov 23 '12 at 15:40
  • That values are not relevant, you can ignore them. They are not useful in the session configuration (except for the MemoryStore and MongoStore). Config is an object with project specific configuration, memoryStore is the default way of saving session objects to memory (not useful for production) – Renato Gama Nov 23 '12 at 16:28
  • Yep I don't follow with MemoryStore sorry. I'm only trying to get my code to work to pass the user's username to the view when they are logged in. At the moment I can't even get the app to run because of errors I don't understand how to fix sorry. – germainelol Nov 23 '12 at 16:36
  • I am getting an error of `user` not being defined here which occurs when I run the program, however I also have a console.log function which does not print in the console as you see in the comments in the other question. ` app.get('/', function(req, res){ res.render('index', {user: req.local.user}) });` – germainelol Nov 23 '12 at 16:55
  • I only have the problem of it not recognising session.user, I have an index.js, login.js and User.js in order to validate the user's login details, but where should I be setting the session once they are logged in so that the view can print their username? I have added index.js to the OP – germainelol Nov 23 '12 at 20:49
  • Take a look at my new edit. Not sure if is it what youre looking for. Hope it helps! – Renato Gama Nov 25 '12 at 17:09