https://github.com/jaredhanson/passport-remember-me
passport.use(new RememberMeStrategy(
function(token, done) {
Token.consume(token, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
return done(null, user);
});
},
function(user, done) {
var token = utils.generateToken(64);
Token.save(token, { userId: user.id }, function(err) {
if (err) { return done(err); }
return done(null, token);
});
}
));
post
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login', failureFlash: true }),
function(req, res, next) {
// issue a remember me cookie if the option was checked
if (!req.body.remember_me) { return next(); }
var token = utils.generateToken(64);
Token.save(token, { userId: req.user.id }, function(err) {
if (err) { return done(err); }
res.cookie('remember_me', token, { path: '/', httpOnly: true, maxAge: 604800000 }); // 7 days
return next();
});
},
function(req, res) {
res.redirect('/');
});
I'm trying to implement remember me feature (above) into my existing application but I couldn't make it. When I add RememberMeStrategy into my login.js, it throws
ReferenceError: RememberMeStrategy is not defined
error. What's missing here?
index.js
var rendering = require('../util/rendering');
var express = require('express');
var router = express.Router();
exports.home = function(req, res) {
res.render('index/index');
};
exports.userHome = function(req, res) {
res.render('index/user-home');
};
login.js
var crypto = require('crypto'),
passport = require('passport'),
passportRememberMe = require('passport-remember-me'),
passportLocal = require('passport-local'),
data = require('../models/auth')();
exports.registerPage = function (req, res) {
res.render('login/register', {username: req.flash('username')});
};
exports.registerPost = function (req, res) {
var vpw = req.body.vpw;
var pwu = req.body.pw;
var un = req.body.un;
req.flash('username', un);
if (vpw !== pwu) {
req.flash('error', 'Your passwords did not match.');
res.redirect('/register');
return;
}
req.checkBody('un', 'Please enter a valid email.').notEmpty().isEmail();
var errors = req.validationErrors();
if (errors) {
var msg = errors[0].msg;
req.flash('error', msg);
res.redirect('/register');
return;
}
var new_salt = Math.round((new Date().valueOf() * Math.random())) + '';
var pw = crypto.createHmac('sha1', new_salt).update(pwu).digest('hex');
var created = new Date().toISOString().slice(0, 19).replace('T', ' ');
new data.ApiUser({email: un, password: pw, salt: new_salt, created: created}).save().then(function (model) {
passport.authenticate('local')(req, res, function () {
res.redirect('/home');
})
}, function (err) {
req.flash('error', 'Unable to create account.');
res.redirect('/register');
});
};
exports.loginPage = function (req, res) {
res.render('login/index', {username: req.flash('username')});
};
exports.checkLogin = function (req, res, next) {
passport.authenticate('local', function (err, user, info) {
if (err || !user) {
req.flash('username', req.body.un);
req.flash('error', info.message);
return res.redirect('/login');
}
req.logIn(user, function (err) {
if (err) {
req.flash('error', info.message);
return res.redirect('/login');
}
req.flash('success', 'Welcome!');
return res.redirect('/home');
});
})(req, res, next);
};
exports.logout = function (req, res) {
req.logout();
req.flash('info', 'You are now logged out.');
res.redirect('/login');
};
routes.js
var rendering = require('./util/rendering'),
indexController = require('./controllers/index'),
loginController = require('./controllers/login');
module.exports = function (app, passport) {
// Home
app.get('/', indexController.home);
app.get('/home', ensureAuthenticated, indexController.userHome);
// Auth
app.get('/register', loginController.registerPage);
app.post('/register', loginController.registerPost);
app.get('/login', loginController.loginPage);
app.post('/login', loginController.checkLogin);
app.get('/logout', loginController.logout);
// 'rendering' can be used to format api calls (if you have an api)
// into either html or json depending on the 'Accept' request header
app.get('/apitest', function(req, res) {
rendering.render(req, res, {
'data': {
'test': {
'testsub': {
'str': 'testsub hello world'
},
'testsub2': 42
},
'test2': 'hello world'
}
});
})
// Auth Middleware
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login');
}
}
server.js
var dbConfig;
try {
// Look for dev conf for local development
dbConfig = require('./config/db.dev.conf.js');
} catch(e) {
try {
// production conf?
dbConfig = require('./config/db.conf.js');
} catch(e) {
console.log('Startup failed. No db config file found.');
return false;
}
}
var knex = require('knex')({
client: 'mysql',
connection: dbConfig
}),
express = require('express'),
bodyParser = require('body-parser'),
cookieParser = require('cookie-parser'),
cookieSession = require('cookie-session'),
serveStatic = require('serve-static'),
expressValidator = require('express-validator'),
flash = require('connect-flash'),
swig = require('swig'),
passport = require('passport'),
passportRememberMe = require('passport-remember-me'),
passportLocal = require('passport-local'),
crypto = require('crypto'),
Bookshelf = require('bookshelf'),
messages = require('./util/messages');
var app = express();
Bookshelf.mysqlAuth = Bookshelf(knex);
app.use(cookieParser('halsisiHHh445JjO0'));
app.use(cookieSession({
keys: ['key1', 'key2']
}));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(expressValidator());
app.use(passport.initialize());
app.use(passport.session());
app.use(passport.authenticate('remember-me'));
app.use(flash());
app.use(serveStatic('./public'));
//app.use(express.favicon(__dirname + '/public/images/shortcut-icon.png'));
app.use(messages());
app.engine('html', swig.renderFile);
app.set('view engine', 'html');
app.set('views', __dirname + '/views');
require('./util/auth')(passport);
require('./routes')(app, passport);
app.listen(process.env.PORT || 3000);
console.log('Listening on port 3000');
That error is simply saying that you haven't defined the RememberMeStrategy function before calling it (you're using new but in Javascript that's just calling a function with a special variable called this). You need to require the module first, in this case:
var RememberMeStrategy = require('passport-remember-me').Strategy;
Just require it in the variable RememberMeStrategy
var RememberMeStrategy= require('passport-remember-me').Strategy;
Related
So I am following a tutorial on how to use JSON tokens and I am getting an error, it was working fine using sessions but I can't figure out why I am having trouble, it is the exact code
this is my authenticate.js file:
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const User = require("./models/user");
const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require("passport-jwt").ExtractJwt;
const jwt = require("jsonwebtoken"); // used to create, sign, and verify tokens
const config = require("./config.js");
exports.local = passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
exports.getToken = function (user) {
return jwt.sign(user, config.secretKey, { expiresIn: 3600 });
}; // config.secretKey is a string of random numbers
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = config.secretKey;
exports.jwtPassport = passport.use(
new JwtStrategy(opts, (jwt_payload, done) => {
console.log("JWT payload:", jwt_payload);
User.findOne({ _id: jwt_payload._id }, (err, user) => {
if (err) {
return done(err, false);
} else if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
})
);
exports.verifyUser = passport.authenticate("jwt", { session: false });
This is my app.js file (the main file):
const createError = require("http-errors");
const express = require("express");
const path = require("path");
const logger = require("morgan");
const config = require("./config");
const indexRouter = require("./routes/index");
const usersRouter = require("./routes/users");
const mongoose = require("mongoose");
const passport = require("passport");
const url = config.mongoUrl;
const connect = mongoose.connect(url, {
useCreateIndex: true,
useFindAndModify: false,
useNewUrlParser: true,
useUnifiedTopology: true,
});
connect.then(
() => console.log("Connected correctly to server"),
(err) => console.log(err)
);
const app = express();
// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// app.use(cookieParser("12345-67890-09876-54321"));
app.use(passport.initialize());
app.use("/", indexRouter);
app.use("/users", usersRouter);
app.use(express.static(path.join(__dirname, "public")));
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page
res.status(err.status || 500);
res.render("error");
});
module.exports = app;
this is the routes/users.js file (I believe the problem is here because I can sign-up (create new users) but I can't login with the same users)
const express = require("express");
const User = require("../models/user");
const passport = require("passport");
const authenticate = require("../authenticate");
const router = express.Router();
/* GET users listing. */
router.get(
"/",
function (req, res, next) {
res.send('send users')
}
);
router.post("/signup", (req, res) => {
User.register(
new User({ username: req.body.username }),
req.body.password,
(err, user) => {
if (err) {
res.statusCode = 500;
res.setHeader("Content-Type", "application/json");
res.json({ err: err });
} else {
if (req.body.firstname) {
user.firstname = req.body.firstname;
}
if (req.body.lastname) {
user.lastname = req.body.lastname;
}
user.save((err) => {
if (err) {
res.statusCode = 500;
res.setHeader("Content-Type", "application/json");
res.json({ err: err });
return;
}
passport.authenticate("local")(req, res, () => {
res.statusCode = 200;
res.setHeader("Content-Type", "application/json");
res.json({
success: true,
status: "Registration Successful!",
});
});
});
}
}
);
});
// I tried to add a console.log inside of the .post() route but it never reach it
router.post(
"/login",
passport.authenticate("local"),
(req, res) => {
const token = authenticate.getToken({ _id: req.user._id });
res.statusCode = 200;
res.setHeader("Content-Type", "application/json");
res.json({
success: true,
token: token,
status: "You are successfully logged in!",
});
}
);
router.get("/logout", (req, res, next) => {
if (req.session) {
req.session.destroy();
res.clearCookie("session-id");
res.redirect("/");
} else {
const err = new Error("You are not logged in!");
err.status = 401;
return next(err);
}
});
module.exports = router;
Basically, every time that i go to localhost:3000/users/login and send a POST request with the username and password, it tells me that I need to use express-session but I am trying to use tokens instead of session
The problem is caused when passport.authenticate('local') is called in routes/users.js file. It is a middleware that automatically calls req.login function in case correct username and password is provided.
The req.login() in turn, implements sessions in order to serialise the user in the session.
You can solve the issue by adding another parameter to passport.authenticate() as passport.authenticate('local', {session: false}. This ensures sessions are not implemented in the 'local' strategy and subsequently login can be performed.
Thanks, it helped me. You have to remove app.use(passort.session) from app.js and do
router.post('/login', passport.authenticate('local', { session: false }), (req, res) => {
var token = authenticate.getToken({ _id: req.user._id });
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.json({ success: true, token: token, status: 'You are successfully logged in!' });
});
I've been running through a problem and I can not find out what it is. I am using Express for this.
I have an "addUser" form which has the following Pug form :
form(action='/adduser', method='POST')
and I have try POST method in the app.js that doesn't work :
app.post("/adduser", function (req, res, info, next){
console.log('This does not appear');
});
What I don't understand is why the following login POST method which is just right above the adduser works perfectly :
app.post("/login", passport.authenticate('local', {
// login things
});
NB : At the beginning, the /adduser was in a router and was also returning 404, that's why I tried to move it in app.js .
Feel free to ask any informations that you need.
Anthony.
FULL CODE
app.js:
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var escape = require('escape-html');
var stock = require('./routes/stock');
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// Variables pour le système d'users
var flash = require('connect-flash');
var crypto = require('crypto');
/* Login script */
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var connection = require('./lib/dbconn');
var sess = require('express-session');
var Store = require('express-session').Store;
var BetterMemoryStore = require(__dirname + '/memory');
var store = new BetterMemoryStore({expires: 60 * 60, debug: true});
app.use(sess({
name: 'StockInfo Sess.',
secret: 'C3ci3stUnSup3rS3cr3t',
store: store,
resave: true,
saveUninitialized: true
}));
//======================================================================================================================
// Configs =============================================================================================================
//======================================================================================================================
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
app.use('/stock', stock);
//passport Strategy -- the express session middleware before calling passport.session()
passport.use('local', new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true //passback entire req to call back
}, function (req, username, password, done) {
if (!username || !password) {
return done(null, false, req.flash('message', 'All fields are required.'));
}
var escapeData = escape(username);
var comboPrenomNom = escapeData.split(".");
var prenom = comboPrenomNom[0].toLowerCase();
var nom = comboPrenomNom[1].toLowerCase();
connection.query("SELECT id, password, salt FROM users WHERE prenom = ? and nom = ?", [prenom, nom], function (err, rows) {
if (err) return done(req.flash('message', err));
if (!rows.length) {
return done(null, false, req.flash('message', 'Invalid username or password.'));
}
if (!(rows[0].password === crypto.createHash('sha256').update(rows[0].salt + escape(password)).digest('hex'))) {
return done(null, false, req.flash('message', 'Invalid username or password.'));
}
req.session.user = rows[0].id;
return done(null, rows[0].id);
});
}
));
passport.serializeUser(function (id, done) {
done(null, id);
});
passport.deserializeUser(function (id, done) {
connection.query("SELECT id, prenom, nom, hasChangedPass, rights FROM users WHERE id = " + id, function (err, rows) {
done(err, rows[0]);
});
});
//======================================================================================================================
// Routes ==============================================================================================================
//======================================================================================================================
app.get('/', function (req, res, next) {
res.redirect('/login');
});
app.get('/login', function (req, res) {
res.render('login/index', {
title: 'LogIn',
message: req.flash('message')
});
});
app.post("/login", passport.authenticate('local', {
successRedirect: '/stock',
failureRedirect: '/login',
failureFlash: true
}), function (req, res, info) {
res.render('login/index', {
'message': req.flash('message')
});
});
app.post("/adduser", function (req, res, info, next) {
if (req.body.prenom && req.body.nom && req.body.password) {
var prenom = escape(req.body.prenom).toLowerCase();
var nom = escape(req.body.nom).toLowerCase();
connection.query("SELECT * FROM users WHERE prenom = ? and nom = ?", [prenom, nom], function (err, rows) {
if (err) {
console.log(err);
}
if (!rows.length) {
var salt = generate_token(32);
var password = escape(req.body.password);
password = crypto.createHash('sha256').update(salt + '' + password).digest('hex');
connection.query("INSERT INTO users (prenom, nom, password, salt) VALUES (?, ?, ?, ?);", [prenom, nom, password, salt], function (err) {
if (err) {
console.log(err);
}
});
}
});
} else {
res.redirect('/stock/fromage');
}
});
app.get('/logout', function (req, res) {
req.session.destroy();
req.logout();
res.redirect('/login');
});
// 404
app.use(function (req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error', {title: err.status});
});
function hasRights(req, res, next) {
if (req.user.rights[0] == 1 || req.user.rights[1] == 2 || req.user.rights[2] == 2 || req.user.rights[3] == 2) {
return next();
}
res.redirect('/stock');
}
function isAuthenticated(req, res, next) {
if (req.session.user) {
if (req.user.hasChangedPass) {
return next();
} else {
res.redirect('/stock/initpass');
}
}
res.redirect('/login');
}
module.exports = app;
/login/register.pug
extends ../layout
block content
include ../templates/header
.wrapper
form(action='/adduser', method='POST')
h3 Nouvel utilisateur
div.form-row
div.form-group
input.form-control(type='text', placeholder='Prénom', name='prenom' required)
div.form-row
div.form-group
input.form-control(type='text', placeholder='Nom', name='nom' required)
div.form-row
div.form-group
input.form-control(type='text', placeholder='Mot de Passe', name='password', value= pass required)
div.form-row
div.form-group
button.form-control Créer
div.form-row
div.form-group
p= rights
In your /adduser route you have 4 parameters in the callback function: (req, res, info, next). Remove info and Express will find your route.
With four parameters Express interprets your handler function as an error handler, see more information here: https://expressjs.com/en/guide/error-handling.html. Because of that you get a 404, as the request is passed through the middleware chain into the 404 handler.
I'm using app.use(express.static(path.join(__dirname,'public'))); to show login page before showing index.html in Node.js.
However, it doesn't show login page before index.html...
I tried to use app.get('/', function (req,res) {res.redirect('/login');}); but it doesn't even go through that app.get and doesn't redirect when it connects to localhost:4000...
my directory setup is shown below
myapp
node_modules
public
images
javascripts
js
stylesheets
index.html
routes
views
login
login.ejs
users
new.ejs
index.jade
layout.jade
app.js
package.json
Also, I want to hold index.html in public folder and just want to display login page before that..
Is there any ways to show that??
Here is my whole code in 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
mongoose.connect("my mongoDB private address");
var db = mongoose.connection;
db.once("open",function () {
console.log("DB connected!");
});
db.on("error",function (err) {
console.log("DB ERROR :", err);
});
var bcrypt = require("bcrypt-nodejs");
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();
}
});
userSchema.methods.authenticate = function (password) {
var user = this;
return bcrypt.compareSync(password,user.password);
};
var User = mongoose.model('user',userSchema);
io = io.listen(server);
app.set("view engine", 'ejs');
app.use(express.static(path.join(__dirname, 'public')));
//setting middleware for login
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());
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
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.'));
}
return done(null, user);
});
}
)
);
//set home routes
//var data_1 = {email:''};
app.get('/', function (req,res) {
res.redirect('/login');
//req.url = '/login';
//next();
});
app.get('/login', function (req,res) {
res.render('login/login',{email:req.flash("email")[0], loginError:req.flash('loginError')});
});
app.post('/login', function(req, res, next) {
passport.authenticate('local-login', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
return res.redirect('/?channel='+ req.body.email);
})(req, res, next);
});
app.get('/logout', function(req, res) {
req.logout();
res.redirect('/login');
});
// set user routes
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]
}
);
}); // new
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');
});
}); // create
//functions
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()){
return next();
}else{
res.redirect('/login');
}
res.redirect('/');
}
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");
}
}
);
}
function handler(req,res){
console.log(req);
fs.readFile(__dirname + '/public/index.html', function(err,data){
if(err){
res.writeHead(500);
return res.end('Error loading index.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 index.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();
});
});
app.listen(4000, function(){
console.log('Server On!!!');
});
Should I use another express to display login page? or what should I do here?
I'm actually newbie in node.js.
Can anybody please help me out here??
Thank you..
EDIT: My whole code for app.js
var express = require('express');
var app = express();
//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 cookieParser = require('cookie-parser');
var methodOverride = require('method-override');
//connecting database
mongoose.connect("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);
});
var bcrypt = require("bcrypt-nodejs");
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();
}
});
userSchema.methods.authenticate = function (password) {
var user = this;
return bcrypt.compareSync(password,user.password);
};
var User = mongoose.model('user',userSchema);
app.set("view engine", 'ejs');
app.use(express.static(path.join(__dirname, 'public')));
//setting middleware for login
app.use(cookieParser());
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());
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 = '';
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);
});
}
)
);
//set home routes
app.get('*', loggedInCheck); ------------------>This is the code with loggedInCheck function. I created another one instead of isLoggedIn function
app.get('/login', function (req,res) {
res.render('login/login',{email:req.flash("email")[0], loginError:req.flash('loginError')});
});
app.post('/login',
function (req,res,next){
next();
}, passport.authenticate('local-login', {
successRedirect : '/posts',
failureRedirect : '/login',
failureFlash : true
})
);
app.get('/logout', function(req, res) {
req.logout();
res.redirect('/login');
});
// set user routes
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]
}
);
}); // new
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');
});
}); // create
app.get('/posts', isLoggedIn, function(req, res){
res.redirect('/status.html?channel=' + global_username);
});
//functions
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()){
console.log("Authenticated");
console.log("Accessing to status.html");
return next();
}else{
console.log("Unauthorized Attempt");
res.redirect('/login');
}
}
function loggedInCheck(req, res, next) {
if (req.isAuthenticated()){
res.redirect('/status.html?channel=' + global_username);
}else{
console.log("Unauthorized Attempt");
res.redirect('/login');
}
}
server.listen(5000);
In Express, the order of calls matters.
In your case, the call to app.use (express.static... is done before the app.get ('/'... so it has a higher priority.
And since express.static ends the middleware chain, it will never call your app.get.
One possible solution would be to place your app.get above the app.use (express.static.
But, if you do so, you will never be able to show your index.html. You could add a condition to choose whether you redirect to /login or call next () to continue the middleware chain.
Edit
After taking a deeper look at your code, you seem to have a middleware isLoggedIn doing the proper logic.
You can keep the order of middlewares unchanged and do app.get('*', isLoggedIn);
This will call your middleware for any GET request.
You want to load '/login' before '/index.html' I assume you want the user to login before they reach the home page.
Also, instead of redirecting, try RENDERING.
Before using this code, reset your isLoggedIn back to default.
app.get('/', function(req, res) {
if(!isLoggedIn)
res.render('login')
else
res.redirect('/');
});
Hie, I split your app.js file into multiple parts in an effort to isolate the router, the files are given below.
app.js
var express = require('./express'),
mongoose = require('./mongoose'),
passport = require('./passport');
var db = mongoose();
var app = express();
var passport = passport();
app.listen(3000, function() {
console.log('Server running on port: ' + 3000);
});
express.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');
module.exports = function() {
io = io.listen(server);
app.set("view engine", 'ejs');
app.use(express.static(path.join(__dirname, 'public')));
//setting middleware for login
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());
require('./passport');
require('./router')(app);
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();
});
});
return app;
};
mongoose.js
var mongoose = require('mongoose');
module.exports = function() {
var db = mongoose.connect("mongodb://localhost/stacktest");
require('./model');
return db;
};
model.js
var mongoose = require('mongoose');
var bcrypt = require("bcrypt-nodejs");
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();
}
});
userSchema.methods.authenticate = function (password) {
var user = this;
return bcrypt.compareSync(password,user.password);
};
mongoose.model('Users', userSchema);
passport.js
var passport = require('passport'),
LocalStrategy = require('passport-local');
module.exports = function() {
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
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.'));
}
return done(null, user);
});
}
)
);
};
And Finally router.js
var passport = require('./passport');
module.exports = function(app) {
app.get('/', function (req, res) {
res.redirect('/login');
//req.url = '/login';
//next();
});
app.get('/login', function (req, res) {
res.render('login', {email: req.flash("email")[0], loginError: req.flash('loginError')});
});
app.post('/login', function (req, res, next) {
passport.authenticate('local-login', function (err, user, info) {
if (err) {
return next(err);
}
if (!user) {
return res.redirect('/login');
}
return res.redirect('/?channel=' + req.body.email);
})(req, res, next);
});
app.get('/logout', function (req, res) {
req.logout();
res.redirect('/login');
});
// set user routes
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]
}
);
}); // new
};
I could not locate the 'checkUserRegValidation' so I had to remove (as I thought it was not directly related to the error in question), I then made two ejs files, with simple heading identifying the page (whether it the login page or index) and I got your expected result (The login page was rendered when put localhost://3000 in my browser). So maybe you could try to isolate your files if it helps, sorry for the long answer.
I'm currently learning node, postgres, and passport but I am not sure on how to secure my application and best practices. I'm not sure on how to sanitize data from client and server or if it's needed but I've parameterized my queries. Any help would be greatly appreciated! Apologies for my newbieness.
var express = require('express');
var helmet = require('helmet')
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var expressSession = require('express-session');
var passport = require('passport');
var passportLocal = require('passport-local');
var pg = require ('pg');
var bcrypt = require('bcryptjs');
var app = express();
app.use(helmet());
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var config = {
user: 'REDACTED',
database: 'REDACTED',
password: 'REDACTED',
host: 'REDACTED',
port: 5432,
max: 10,
idleTimeoutMillis: 30000,
};
var pool = new pg.Pool(config);
pool.on('error', function (err, client) {
console.error('idle client error', err.message, err.stack)
})
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(expressSession( {
secret: process.env.SESSION_SECRET || 'secret',
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new passportLocal.Strategy(function(username, password, done) {
pool.connect(function(poolErr, poolClient, poolDone) {
if(poolErr) {
return console.error('pool client fetch error', poolErr);
}
poolClient.query('SELECT id, password FROM users WHERE username = $1', [username], function(queryErr, queryRes) {
if(queryErr) {
return console.error('query error', queryErr);
}
if(queryRes.rows[0] != undefined)
{
bcrypt.compare(password, queryRes.rows[0].password, function(compareErr, compareRes) {
if(compareErr) {
return console.error('bcrypt error', compareErr);
}
if(compareRes) {
done(null, { id: queryRes.rows[0].id });
} else {
done(null, null);
}
poolDone();
});
} else {
//user not found
poolDone();
done(null, null);
}
});
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
pool.connect(function(err, poolClient, poolDone) {
if(err) {
return console.error('pool client fetch error', err);
}
poolClient.query('SELECT * FROM users WHERE id = $1', [id], function(queryErr, queryRes) {
if(queryErr) {
return console.error('query error', queryErr);
}
if(queryRes.rows[0] != undefined)
{
done(null, { id: id, username: queryRes.rows[0].username, firstname: queryRes.rows[0].firstname, lastname: queryRes.rows[0].lastname });
} else {
//user not found
done(null, null);
}
poolDone();
});
});
});
app.set('view engine', 'ejs');
app.get('/', function(req, res) {
res.render('index', {
isAuthenticated: req.isAuthenticated(),
user: req.user
})
});
app.get('/login', function(req, res) {
res.render('login');
});
app.post('/login', passport.authenticate('local'), function(req, res) {
res.redirect('/');
});
app.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
server.listen(8080);
console.log('Server is running...');
I don't see much problem but here is my 2 cent to improve it.
Use compression to reduce network size
app.use(require('compression'));
Use serve-static for static file like images or pdf
app.use(require('serve-static')('./public'))
Use middleware to control end point that need Authentication like view/edit profile, change password and etc
function authenticated(req, res, next) {
if (req.isAuthenticated()) next();
else {
res.status(401).send('User not authenticated.');
// or redirect to login
}
}
app.get('/profile', authenticated, function(req, res) {
res.render('profile', {
req.user
})
})
Include session destroy when logout
app.get('/logout', function(req, res) {
req.session.destroy();
req.logout();
res.redirect('/');
});
for sanitizing queries you can use ORM like sequelise
The error in question
Error: Can't set headers after they are sent.
server.js
// set up ======================================================================
var express = require('express')
, app = express()
, cookieParser = require('cookie-parser')
, bodyParser = require('body-parser')
, expressSession = require('express-session')
, server = require('http').createServer(app)
, passport = require('passport')
, local = require('passport-local').Strategy
, md5 = require('md5')
, util = require('util')
, flash = require('connect-flash')
, port = 80
, url = require('url')
, db = require('./db');
// optimization ================================================================
var compress = require('compression');
app.use(compress());
// configuration ===============================================================
app.use(express.static('public'));
app.use(cookieParser());
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.use(expressSession({
secret: 'key',
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
// passport ====================================================================
passport.use(new local(
function(username, password, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
db.findUserByName(username, function (err, user) {
if (err) { done(err); }
if (!user) { done(null, false, {message: 'Unknown user: ' + username})}
if (md5(password) == user.password) {
done(null, user);
} else {
done(null, false, {message: 'Invalid username or password'});
}
});
});
}
));
passport.serializeUser(function(user, done) {
done(null, user.uuid);
});
passport.deserializeUser(function (id, done) {
db.findUserByUUID(id, done);
});
// routes ======================================================================
require('./routes')(app, passport);
// launch ======================================================================
server.listen(port, function(){
console.log('server started');
});
routes.js
var flash = require('connect-flash');
module.exports = function(app, passport) {
app.get('/', function(req, res) {
res.render('index', { num: 0, logged: false });
});
app.get('/login', function (req, res) {
if (typeof req.user !== 'undefined') {
// User is logged in.
res.redirect('/');
} else {
req.user = false;
var message = req.flash('error');
if (message.length < 1) {
message = false;
}
res.render('login', { logged: false, message: message });
}
});
app.post('/login',
passport.authenticate('local', {
failureRedirect: '/login',
failureFlash: true
}),
function(req, res) {
res.redirect('/');
}
);
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
};
db.js
var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
var ObjectId = require('mongodb').ObjectID;
var url = 'mongodb://localhost:27017/db';
exports.findUserByName = function (username, callback) {
MongoClient.connect(url, function(err, db) {
var cursor = db.collection('players').find( { "name": username } );
cursor.each(function(err, doc) {
if (doc != null) {
console.log('YES');
callback(false, doc)
} else {
console.log('NO');
callback(false, null);
}
db.close();
});
});
};
var findUserByUUID = function(uuid, db, callback) {
var cursor = db.collection('players').find( { "uuid": uuid } );
cursor.each(function(err, doc) {
assert.equal(err, null);
if (doc != null) {
callback(false, doc)
} else {
callback(false, null);
}
});
};
exports.findUserByUUID = function (uuid, callback) {
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
findUserByUUID(uuid, db, function(err, data) {
callback(err, data);
db.close();
});
});
};
I understand that i'm calling done() twice, but when? I'm unable to find it.
I suppose that the problem is on the routes, but I don't know where.
In your passport.use() handler, each time you call done(), you need to then return so that the code after it is not also executed.
Right now you have a series of if statements that can each call done(), but they aren't if/else statements so more than one condition you are testing for can be met and thus done() can be called more than once.
For example, change this:
if (!user) { done(null, false, {message: 'Unknown user: ' + username})}
to this:
if (!user) {
done(null, false, {message: 'Unknown user: ' + username});
return;
}
And, change this:
if (err) { done(err); }
to this:
if (err) {
done(err);
return;
}
Or, you could make everything into if/else if/else if/else such that only one branch could ever execute. The problem occurs when more than one of your if statements has its condition met and then is executed.
Now, your findUserByName() calls its callback in cursor.each() so it can call it multiple times which then causes you to call done() multiple times.