I've made a basic website with nodejs and express that needs a session system so i used express-session to do that. The issue is that my session is not kept through the different pages of my website, it seems like the req.session is directly deleted after the login has been done.
Here is my code (just the important parts):
var express = require('express');
var consolidate = require('consolidate');
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/mydb";
var bodyparser = require('body-parser');
var session = require('express-session');
//var MongoStore = require('connect-mongo')(session);
var app = express();
app.engine('html', consolidate.swig);
app.set('view engine', 'html');
app.use(express.static(__dirname + '/views'));
app.use(bodyparser.json());
app.use(bodyparser.urlencoded({extended:true}));
app.use(session({
secret: "stfkjvctygf",
resave : false,
saveUninitialized : true,
cookie: { secure: false, maxAge: 1800000 }
}));
app.post('/login',function(req,res,next){
MongoClient.connect(url, function(err, db){
if(err) throw err;
var dbo=db.db("testdb");
var user=dbo.collection("login").findOne({userid : req.body.userid, pwd : req.body.pwd}, function(err, user){
if(err) throw err;
if (user) {
req.session.user = user;
req.session.save(function() {
console.log("current user session: "+req.session.user.userid);
res.render('mainpage.html', {userid: req.session.user.userid});
});
}
else {
console.log("connection failed");
res.render('login.html', {error: 'Invalid username or password'});
}
db.close();
});
});
});
app.get('/mainpage', function(req,res,next) {
if (req.session.user) {
res.render('mainpage.html', {userid: req.session.user.userid});
}
else {
return res.status(401).send();
}
});
app.get('/profilpage', function(req,res,next){
res.render('profilpage.html', {userid: req.session.user.userid});
});
app.listen(8080);
So when the log in of an user succeeds, mainpage.html is displayed properly (the field userid is correctly field with req.session.user.userid), but when I click on another page like the profilpage or if I come back to the mainpage, the session is not saved and the userid field is incorrect.
I also want to mention that the if (req.session.user) clause
in /mainpage does not work, the else seems to be never used.
(The mongodb database and html files are correct)
Thank you.
Delete 'var user =' because you dont need to assign the query to a variable since it is using a callback for returned data.
dbo.collection("login").findOne({userid : req.body.userid, pwd : req.body.pwd}, function(err, user){
if(err) throw err;
console.log(user) //It is most likely an array. Then use index to point the session to the username as such req.session.user = user[0].username
if (user) {
req.session.user = user;
req.session.save(function() {
console.log("current user session: "+req.session.user.userid);
res.render('mainpage.html', {userid: req.session.user.userid});
});
}
else {
console.log("connection failed");
res.render('login.html', {error: 'Invalid username or password'});
}
In addition, 'if(user)' is a bad practice, because if mongodb returns an empty array, that condition is still true, so that causes error in your logic. Its better to do something like
if(data.length > 0){
//your code here. data is what is returned from your mongo Query
}
Related
I'm currently using Node.js along with Mongoose, Express-Session and Passport. This is all to develop the Authentication side of my project.
Today I ran into an issue: When trying to login an user, their Id is undefined.
Here is the piece of code that I think is related to this issue:
var User = require("./user-model"); //user model located in another file
var passport = require("passport");
var session = require("express-session");
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.post("/register", (req, res) => {
var user = new User(req.body);
user.save()
.then(item => {
User.findOne().sort({_id: 1}).exec(function(err, results, fields) {
if(err) throw err;
const user_id = results[0]; //Here, the console logs "undefined"
console.log(results[0]);
req.login(user_id, function(err) {
console.log("User saved to database");
res.redirect('/');
});
});
})
.catch(err => {
console.log(400).send("Unable to save to database");
});
});
passport.serializeUser(function(user_id, done) {
done(null, user_id);
});
passport.deserializeUser(function(user_id, done) {
done(null, user_id);
});
If you need any extra piece of code, I'll glady update this for you.
Thanks in advance!
findOne finds one result, hence the name.
So results parameter is in fact user, and it likely should be:
User.findOne().sort({_id: 1}).exec(function(err, user) {
if(err) throw err;
const user_id = user._id;
...
Note that user can be null in case there's none.
I'm a newbie in node.js and I'm trying to redirect all the routes after localhost:4000/ if it is not logged in. And it gives me error with "Too many redirects"...
my code that using app.get in app.js
app.get('*', loggedInCheck);
and below code is loggedInCheck function that I've written,
function loggedInCheck(req, res, next) {
if (req.isAuthenticated()){
res.redirect('/status');
}else{
console.log("Please Log in to access to this webpage");
res.redirect('/login');
}
}
However, it keeps giving me an error as "Too many redirects" and doesn't go through login page because it is not authenticated yet.
What is my problem here? and how can I fix this....?
Can anybody help me out here??
Just in case, I'll put my whole code from app.js
app.js
var io = require('socket.io');
var express = require('express');
var app = express();
var redis = require('redis');
var sys = require('util');
var fs = require('fs');
//Added for connecting login session
var http = require('http');
var server = http.createServer(app);
var path = require('path');
var mongoose = require('mongoose');
var passport = require('passport');
var session = require('express-session');
var flash = require('connect-flash');
var async = require('async');
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
//Connecting Database (MongoDB)
mongoose.connect("my private mongoDB address");
var db = mongoose.connection;
db.once("open",function () {
console.log("DB connected!");
});
db.on("error",function (err) {
console.log("DB ERROR :", err);
});
//Setting bcrypt for password.
var bcrypt = require("bcrypt-nodejs");
//Setting userSchema for MongoDB.
var userSchema = mongoose.Schema({
email: {type:String, required:true, unique:true},
password: {type:String, required:true},
createdAt: {type:Date, default:Date.now}
});
userSchema.pre("save", function (next){
var user = this;
if(!user.isModified("password")){
return next();
} else {
user.password = bcrypt.hashSync(user.password);
return next();
}
});
//setting bcrypt for password.
userSchema.methods.authenticate = function (password) {
var user = this;
return bcrypt.compareSync(password,user.password);
};
//Setting User as userSchema.
var User = mongoose.model('user',userSchema);
io = io.listen(server);
//Setting middleware for login format.
app.set("view engine", 'ejs');
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}));
app.use(methodOverride("_method"));
app.use(flash());
app.use(session({secret:'MySecret', resave: true, saveUninitialized: true}));
app.use(passport.initialize());
app.use(passport.session());
//Initializing passport.
passport.serializeUser(function(user, done) {
//console.log('serializeUser()', user);
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
//console.log('deserializeUser()', user);
User.findById(id, function(err, user) {
done(err, user);
});
});
var global_username = ''; //Global variable for username to put in the address
//Initializing passport-local strategy.
var LocalStrategy = require('passport-local').Strategy;
passport.use('local-login',
new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
User.findOne({ 'email' : email }, function(err, user) {
if (err) return done(err);
if (!user){
req.flash("email", req.body.email);
return done(null, false, req.flash('loginError', 'No user found.'));
}
if (!user.authenticate(password)){
req.flash("email", req.body.email);
return done(null, false, req.flash('loginError', 'Password does not Match.'));
}
var email_address = req.body.email;
var username = email_address.substring(0, email_address.lastIndexOf("#"));
global_username = username;
return done(null, user);
});
}
)
);
//Check whether it is logged in or not.
//If it is not logged in(Session is out), it goes to login page
//If it is logged in(Session is still on), it goes directly to status.html
app.get('*', loggedInCheck);
app.get('/login', function (req,res) {
res.render('login/login',{email:req.flash("email")[0], loginError:req.flash('loginError')});
});
//Accessing to MongoDB to check to login or not
app.post('/login',
function (req,res,next){
next();
}, passport.authenticate('local-login', {
successRedirect : '/status',
failureRedirect : '/login',
failureFlash : true
})
);
//Logging out
app.get('/logout', function(req, res) {
req.logout();
console.log("Logging out the account!");
res.redirect('/login');
});
//Creating new account
app.get('/users/new', function(req,res){
res.render('users/new', {
formData: req.flash('formData')[0],
emailError: req.flash('emailError')[0],
passwordError: req.flash('passwordError')[0]
}
);
});
//If creating an account is successed, then goes back to login page.
app.post('/users', checkUserRegValidation, function(req,res,next){
User.create(req.body.user, function (err,user) {
if(err) return res.json({success:false, message:err});
res.redirect('/login');
});
});
//Calling status.html
app.get('/status', isLoggedIn, function(req, res){
res.redirect('/status.html?channel=' + global_username);
});
//Calling Topology_view html
app.get('/topology', isLoggedIn, function(req, res){
console.log("Accessing to topology_view");
res.redirect('topology.html?channel=' + global_username);
});
//functions
//Check whether session is still on or not.
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()){
console.log("Authenticated");
return next();
}else{
console.log("Unauthorized Attempt");
res.redirect('/login');
}
}
//Initial checking whether session is on or not.
function loggedInCheck(req, res, next) {
if (req.isAuthenticated()){
res.redirect('/status');
}else{
console.log("Please Log in to access to this webpage");
res.redirect('/login');
}
}
//Checking whether email is already in the database or not in sign up.
//If email is already in the database, it gives error message.
function checkUserRegValidation(req, res, next) {
var isValid = true;
async.waterfall(
[function(callback) {
User.findOne({email: req.body.user.email, _id: {$ne: mongoose.Types.ObjectId(req.params.id)}},
function(err,user){
if(user){
isValid = false;
req.flash("emailError","- This email is already resistered.");
}
callback(null, isValid);
}
);
}], function(err, isValid) {
if(err) return res.json({success:"false", message:err});
if(isValid){
return next();
} else {
req.flash("formData",req.body.user);
res.redirect("back");
}
}
);
}
//handler function is for topology.html.
function handler(req,res){
fs.readFile(__dirname + '/public/topology.html', function(err,data){
if(err){
res.writeHead(500);
return res.end('Error loading topology.html');
}
res.writeHead(200);
console.log("Listening on port 3000");
res.end(data);
});
fs.readFile(__dirname + '/public/style.css', function(err,data){
if(err){
res.writeHead(500);
return res.end('Error loading topology.html');
}
res.writeHead(200);
console.log("Listening on port 3000");
res.end(data);
});
}
io.sockets.addListener('connection', function(socket){
console.log("connceted : " + socket.id);
var subscriber = redis.createClient(6379, 'localhost');
subscriber.psubscribe("*");
subscriber.on("pmessage", function(pattern, channel, message) {
//console.log(message);
socket.emit(channel, message);
});
socket.on('disconnect', function () {
console.log("disconnceted : " + socket.id);
subscriber.quit();
});
socket.on('close', function() {
console.log("close");
subscriber.quit();
});
});
server.listen(4000);
Your issue is in your loggedInCheck function. No matter what route you are on, you are checking if the user is authenticated otherwise redirect to login. So, even if your trying to get to the login page, it's gonna try and redirect again, and again forever.
app.get('*', loggedInCheck);
Isn't a good way todo it. You should have some sort of function that makes sure your not trying to go to a zone that is okay for non-users. Maybe something like this:
app.get('*', function(req, res, next){
if(req.url != '/login'){
loggedInCheck(req, res, next);
}else{
next();
}
});
Preface: I'm new to node.js, express, socket.io and all of that. I realize my code is kinda messy and needs to be separated out into modules, etc -- but I've not done that yet because I was trying to get the authentication part working first. I have searched all over stackoverflow and other sites. I've found some promising examples, but I've just not been able to make it work.
I followed a couple of tutorials to create my express app which allows a user to register, login, and view their details. I also followed a tutorial which helped me make a basic socket.io chat. What I'm trying to do is combine them and have the user log in and then be redirected to the chat app. The problem I have is that when I redirect them, I have no way to know "who they are" on the socket.io side of things. Currently I've got it set up so you have to enter your name to chat -- I would like it to grab the info from the session and use that instead.
Currently when a user logs in, it does set a cookie (I can view it in the console). So I know the cookie is there. It also sets the info into the MongoStore. I've verified that as well with db.collection.find().
Here's the code that I have so far. If any of the experts out there could help me find a way to pass the session info over to socket.io, I would very much appreciate it!
var mongo = require('mongodb').MongoClient;
var bodyParser = require('body-parser');
var bcrypt = require('bcryptjs');
var csrf = require('csurf');
var path = require ('path');
var express = require('express');
var mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');
var session = require('express-session');
var moment = require('moment');
var now = moment().format('L');
var http = require('http');
var MongoStore = require('connect-mongo')(session);
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
UserSchema = new Schema({
//id: ObjectId,
firstName: String,
lastName: String,
username: {
type: String,
unique: true,
uniqueCaseInsensitive:true
},
password: String,
email: {
type:String,
unique: true,
uniqueCaseInsensitive:true
},
accountType: String,
accountStatus: String,
acctActivation:{
type:String,
unique:true
},
joinDate: String
});
UserSchema.plugin(uniqueValidator,{ message: 'Error, {PATH} {VALUE} has already been registered.\r' });
var User = mongoose.model('User', UserSchema);
var app = express();
app.engine('ejs', require('ejs').renderFile);
app.locals.pretty = true;
//connect to mongo
mongoose.connect('mongodb://localhost/myUserDb');
//create server
var server = http.createServer(app).listen(3000);
var client = require('socket.io')(server);
console.log('listening on port 3000');
//middleware
app.use(express.static('public'));
app.use(bodyParser.urlencoded({extended:true}));
app.use(session({
secret: 'mysecret!',
resave:false,
saveUninitialized: false,
stringify:true,
store: new MongoStore({
url: 'mongodb://127.0.0.1/sid2'
})
}));
app.use(csrf());
app.use(function(req,res,next){ // check to see if user already has a session, if so, query mongodb and update the user object
if(req.session && req.session.user){
User.findOne({email: req.session.user.email}, function(err, user){
if(user){
req.user = user;
delete req.user.password; // remove password field from session
req.session.user = req.user;
res.locals.user = req.user;
}
next();
});
}else{
next();
}
});
function requireLogin(req,res,next){ // check to see if user is logged in, if not, boot em
if(!req.user){
res.redirect('/login');
}else{
next();
}
};
function requireAdmin(req,res,next){ // check to see if accountType = Developer (or admin later) - if not, send them to dashboard
if(req.user.accountType !== 'Developer'){
res.redirect('/dashboard');
}else{
next();
}
};
app.get('/', function(req, res){
if(req.user){
res.render('dashboard.ejs');
}else{
res.render('index.ejs');
}
});
app.get('/register', function(req,res){
res.render('register.ejs', {csrfToken: req.csrfToken(),
error:false});
});
app.post('/register', function(req,res){
var hash = bcrypt.hashSync(req.body.password, bcrypt.genSaltSync(10));
var user = new User({
firstName: req.body.firstName,
lastName: req.body.lastName,
username: req.body.username,
password: hash,
email: req.body.email,
accountType: 'Standard',
accountStatus: 'Active',
joinDate: now
});
user.save(function(err){
if(err){
console.log(err);
res.render('register.ejs', {csrfToken: req.csrfToken(),
error: err});
}else{
req.session.user = user;
res.redirect('/dashboard');
}
});
});
app.get('/login', function(req,res){
res.render('login.ejs', {
csrfToken: req.csrfToken(),error:false});
});
app.post('/login', function(req, res){
User.findOne({username: {$regex: new RegExp('^' + req.body.username, 'i')}}, function(err, user){
if(!user){
res.render('login.ejs', {error: 'Invalid username or password combination.',
csrfToken: req.csrfToken()});
}else{
if(bcrypt.compareSync(req.body.password, user.password)){
req.session.user = user;
res.redirect('/chat');
}else{
res.render('login.ejs', {error: 'Invalid username or password combination.',
csrfToken: req.csrfToken()});
}
}
});
});
app.get('/dashboard', requireLogin, function(req,res){
res.render('dashboard.ejs');
});
app.get('/chat', requireLogin, function(req,res){
res.render('chat.ejs');
});
app.get('/admin', requireLogin, requireAdmin, function(req,res){ //required logged in AND admin status
// var userlist = User.find({});
User.find({},{},function(err,docs){
res.render('admin.ejs',{ "userlist": docs
});
}) ;
// res.render('admin.ejs');
});
app.get('/logout', function(req,res){
req.session.reset();
res.redirect('/');
});
mongo.connect('mongodb://127.0.0.1/chat', function(err,db){
if(err) throw err;
client.on('connection', function(socket){
var col = db.collection('messages');
sendStatus = function(s){
socket.emit('status', s);
};
//emit all messages (shows old room data)
col.find().limit(100).sort({_id: 1}).toArray(function(err, res){
if(err) throw err;
socket.emit('output',res);
});
//wait for input
socket.on('input', function(data){
var name = data.name,
message = data.message,
whitespacePattern = /^\s*$/;
if(whitespacePattern.test(name) || whitespacePattern.test(message)){
sendStatus('Name and message is required.');
}else{
col.insert({name: name, message: message}, function(){
//emit latest message to all clients
client.emit('output', [data]);
sendStatus({
message: "Message sent",
clear: true
});
});
}
});
});
});
Ok, so I've finally figured it out. I used express-session to set the cookie, and then a module called express-socket.io-session to get it over to socket.io.
From there, I was able to use:
var data = socket.handshake.session;
console.log(data.user.username);
to retrieve the values I needed. All of these days searching and it was a very simple solution. I guess I just needed some sleep!
I am trying to create a sign up where the user if already existing in the db is logged into the system, or else a new user is created in the system.
So far I have come up with the following code.
//filename passport-config
var config = require('./config');
var passport = require('passport');
var User = require('./models/user');
var LocalStrategy = require('passport-local').Strategy;
var isValidPassword = function(user, password){
return bCrypt.compareSync(password, user.password);
};
// Generates hash using bCrypt
var createHash = function(password){
return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
}
// As with any middleware it is quintessential to call next()
// if the user is authenticated
var isAuthenticated = function (req, res, next) {
if (req.isAuthenticated())
return next();
res.redirect('/');
}
passport.use('signup', new LocalStrategy({
passReqToCallback : true
},
function(req, email, password, done) {
findOrCreateUser = function(){
// find a user in Mongo with provided email
User.findOne({'email':email},function(err, user) {
// In case of any error return
if (err){
console.log('Error in SignUp: '+err);
return done(err);
}
// already exists
if (user) {
User.findOne({ 'email' : email },
function(err, user) {
if (!user){
console.log('User Not Found with email '+email);
return done(null, false);
}
// User exists but wrong password, log the error
if (!isValidPassword(user, password)){
console.log('Invalid Password');
return done(null,false);
}
});
} else {
// if there is no user with that email
// create the user
var newUser = new User();
// set the user's local credentials
newUser.email = email;
newUser.password = createHash(password);
// save the user
newUser.save(function(err) {
if (err){
console.log('Error in Saving user: '+err);
throw err;
}
console.log('User Registration succesful');
return done(null, newUser);
});
}
});
};
// Delay the execution of findOrCreateUser and execute
// the method in the next tick of the event loop
process.nextTick(findOrCreateUser);
})
);
my router
router.post('/signup', passport.authenticate('signup', {
successRedirect: '/timeslot',
failureRedirect: '/'
}));
my server.js file
var express = require('express');
var bodyParser = require('body-parser');
var leisure = require('leisure');
var cors = require('cors');
var passport = require('passport');
var config = require('./config');
var passportConfig = require('./passport-config');
var session = require('express-session')
var expressHbs = require('express-handlebars');
var mediaTypes = [
{ contentType: 'application/hal+json' },
{ contentType: 'application/json' },
{ contentType: 'text/html' }
];
var app = express();
/*Handlebars */
app.engine('handlebars', expressHbs({layout: false}) );
app.set('view engine', 'handlebars');
app.use(express.static(__dirname + '/assets'));
app.use(cors(config.settings.cors));
app.use(bodyParser());
app.use(leisure.accept(mediaTypes));
/*sessions */
app.use(session({
secret: 'keyboardSFS23432##!#!#at'
}));
app.use(passport.initialize());
app.use(passport.session());
var routes = require('./routes');
app.use('/', routes.router);
function start () {
var port = process.env.PORT || 3000;
app.listen(port);
console.log('Appoints service started on port ' + port);
}
exports.app = app;
exports.start = start;
The signup route doesn't work at all and I am pretty confused on how to debug this, any suggestions will be appreciated.
Have a look at the excellent article at
http://scotch.io/tutorials/javascript/easy-node-authentication-setup-and-local
and the sample code at
https://github.com/scotch-io/easy-node-authentication (with MongoDB), or
https://github.com/tobilg/easy-node-authentication-redis (with Redis as backend)
I have a PEAN (Postgre, Express, Angular, NodeJS) Stack Application. The web services are written using
Node and Express
Consumed in AngularJS service.js using factory methods.
Now For eg,When I hit the URL deployed on Heroku account like xyz.herokuapp.com/getXYZ then I can actually see all the JSON data on the Browser.
Is their anyway I can put a layer between what a layman hits an URL and what data is served to him, or is it the way it has to work.
Appreciate all the responses. I've checked with OAuth npm package but it provides the basic session management facilities, Is there anything specific that can serve my purpose?
I'm trying to implement something similar,
you can use passport local strategy with JSON Web Token
/login/ is supplied with username and password, if both correct it will send JWT
then you send the token received with each request to a protected router
sample code:
var pg = require('pg');
var express = require('express');
var bodyParser = require('body-parser');
var passport = require('passport');
var jwt = require('jsonwebtoken');
var jwtcheck = require('express-jwt');
var LocalStrategy = require('passport-local').Strategy;
var app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(passport.initialize());
app.use(express.static(__dirname + '/client'));
secret = 'aMdoeb5ed87zarRdkD6greDZL81DcnrzeSD648ferFejmplx';
var port = process.env.PORT || 8080; // set our port
var User = [
{ id: 1, Username: 'bob', password: 'secret', email: 'bob#example.com',apikey: 'bob' }
, { id: 2, Username: 'joe', password: 'birthday', email: 'joe#example.com',apikey: 'gfsdgsfgsfg' }
];
function findByApiKey(apikey, fn) {
for (var i = 0, len = User.length; i < len; i++) {
var user = User[i];
if (user.apikey === apikey) {
return fn(null, user);
}
}
return fn(null, null);
}
passport.use('login', new LocalStrategy(
function(username, password, done) {
findByApiKey(username, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
return done(null, user);
});
}
));
app.post('/login', function(req, res, next) {
passport.authenticate('login', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
return res.json(401, { error: 'message' });
}
var token = jwt.sign({ username: 'somedata'}, secret, { expiresInMinutes: 2 });
res.json({ token : token });
})(req, res, next);
});
app.get('/db/:db/schema/:schema/relation/:relation/:id',
jwtcheck({secret: secret}),
function(req, res) {
var conString = "postgres://ali#localhost/rest";
var client = new pg.Client(conString);
client.connect(function(err) {
if(err) {
return console.error('could not connect to postgres', err);
}
client.query('SELECT * from ' + req.params.schema +'.'+ req.params.relation , function(err, result) {
if(err) {
return console.error('error running query', err);
}
res.send(JSON.stringify(result.rows));
client.end();
});
});
});
app.listen(port);
If your goal is to deliver different content to a human accessing the api URL with a browser, you can have your Express site check the request header Accept. If Accept is "text/html" service a HTML page with whatever it is you want (data formatted in a layout or API documentation for that method etc.), if Accept is "application/json" serve the API Data.
If it's security and authentication you're after, that's a different matter :-)