Node + Passport - multiple users - node.js

When trying to implement the example code from the passport guide, I'm running into an issue where the most recently logged in user "replaces" all others. For example:
user1 logs in and leaves a note as user1
user2 logs in
now when user1 leaves a note, it's posted as user2
Do the user sessions need to be stored in a database with something like connect-mongo or does passport keep track of individual sessions? It seems like the API calls are always getting the req.user for the most recent user, regardless of which user makes it.
A similar question had a problem with the serializer. I'm not sure where my problem lies so I'll just post it all.
// Express setup
var http = require('http');
var express = require('express');
var app = express();
var signedIn = false;
// Mongoose setup
var mongoose = require('mongoose');
mongoose.connect('');
var UserSchema = mongoose.Schema({
username: String,
password: String
});
var NoteSchema = mongoose.Schema({
text: String,
user: String
});
// Used for password authorization
UserSchema.methods.validPassword = function (pwd) {
return (this.password === pwd);
};
var Users = mongoose.model('Users', UserSchema);
var Notes = mongoose.model('Notes', NoteSchema);
// Passport setup
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
// Passport Serialize
passport.serializeUser(function (user, done) {
done (null, user.id);
});
passport.deserializeUser(function (id, done) {
Users.findById(id, function (err, user) {
done (err, user);
});
});
// Use Local Strategy
passport.use(new LocalStrategy(
function(username, password, done) {
Users.findOne({ username: username }, function (err, user) {
if (err) {
return done(err); }
if (!user) {
return done(null, false, {message: 'Incorrect username' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password' });
}
console.log(user.username + " is signed in");
return done(null, user);
});
}
));
// App configuration
app.configure(function () {
app.set('port', process.env.PORT || 5000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.static(__dirname + '/public'));
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({ secret: 'keyboard cat' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
})
// Passport Authentication
app.post('/login',
passport.authenticate('local', { successRedirect: '/notes',
failureRedirect: '/login',
/*failureFlash: true*/ })
);
// API
// Notes
app.get('/api/notes', function (req, res) {
Notes.find({}, function (err, note) {
if (err) {
res.send(err);
}
res.json(note);
})
})
app.post('/api/notes', function (req, res) {
Notes.create({
text : req.body.text,
user : req.user.username,
done : false
}, function (err, note) {
if (err) {
res.send(err);
}
Notes.find(function (err, note) {
if (err) {
res.send(err);
}
res.json(note);
})
})
})
app.delete('/api/notes/:note_id', function (req, res) {
Notes.remove({ _id : req.params.note_id },
function (err, req) {
if (err) {
res.send(err);
}
Notes.find(function (err, note) {
if (err) {
res.send(err);
}
res.json(note);
});
});
});
// Users
// Create New User
app.post('/api/users', function (req, res, next) {
Users.create({
username : req.body.username,
password : req.body.password,
done : false
}, function (err, user) {
if (err) {
res.send(err);
} else {
res.redirect('/login');
}
});
});
// Routes
app.get('/', function (req, res) {
res.render('login');
});
app.get('/login', function (req, res) {
res.render('login');
})
app.get('/newuser', function (req, res) {
res.render('newuser');
})
app.get('/notes', function (req, res) {
if (req.user != null) {
console.log(req.user);
res.render('index', { 'userName' : req.user.username });
} else {
res.send("Not signed in!")
}
});
// HTTP Server
http.createServer(app).listen(app.get('port'), function() {
console.log("Express server listening on: " + app.get('port'));
})

I can think of several reasons why this might happen:
when you (mistakenly) create a global variable to hold some form of state information, like your suspicious looking signedIn variable (you don't seem to be doing that in the posted code though);
using app.locals where you meant to be using res.locals (you're also not doing that, but still worth a mention);
you're logging in as a second user from a different tab/window from the same browser; sessions are shared across tabs/windows, so when you log in as UserA from one tab, and subsequently log in as UserB from another, you can't perform actions as UserA anymore before that session was overwritten by the UserB session; try logging in as UserB from a different browser;

Related

My Web App Keeps Logging Me Out Node.js Express.js express-session

My website keeps logging me out when I try to access other pages when logged in. I am using cookies to store the session but, I guess I did not do it properly cause even after authentication, I am being continuously logged out. It is not letting me use my app.
Here is the link to my web-app: https://upset-gilet-lion.cyclic.app/
Here is my code:
require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");
const ejs = require("ejs");
const mongoose = require("mongoose");
const session = require("express-session");
const passport = require("passport");
const passportLocalMongoose = require("passport-local-mongoose");
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const findOrCreate = require("mongoose-findorcreate")
const app = express();
app.use(express.static(__dirname + "/public/"));
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({
extended: true
}));
// Initializing Session
app.use(session({
secret: process.env.SECRET,
resave: false,
saveUninitialized: false
}));
// Initializing Passport
app.use(passport.initialize());
app.use(passport.session());
// Connecting to MongoDB using Mongoose
mongoose.set('strictQuery', true);
const mongodb = "mongodb://127.0.0.1:27017/userDB"
const mongodbNet = "mongodb+srv://admin-bhoami:"+ process.env.PASSWORD +"#secretscluster.uztbzho.mongodb.net/userDB";
mongoose.connect(mongodbNet, function(err){
if (err) {
console.log('Unable to connect to MongoDB');
console.log(err);
} else {
const PORT = process.env.PORT || 3000;
app.listen(PORT, function() {
console.log("Server started on Port ${PORT}");
console.log('Successfully connected to MongoDB');
});
}
});
// Creating a new User Database
const userSchema = new mongoose.Schema ({
email: String,
password: String,
googleId: String,
secret: Array
});
userSchema.plugin(passportLocalMongoose);
userSchema.plugin(findOrCreate);
const User = new mongoose.model("User", userSchema);
// Set Local Login Strategy
passport.use(User.createStrategy());
// Serialize and Deserialize
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
// Google Authentication Strategy that authenticates users using a Google Account and OAuth 2.0 tokens.
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "https://upset-gilet-lion.cyclic.app/auth/google/secrets", //"http://localhost:3000/auth/google/secrets"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({username: profile.emails[0].value, googleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
// Home Page
app.route("/")
.get(function(req, res) {
res.render("home");
});
// Google Authentication Route
app.get('/auth/google',
passport.authenticate('google', { scope: ["openid", "profile", "email"] }));
app.get("/auth/google/secrets",
passport.authenticate("google", { failureRedirect: "/signin" }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect("/secrets");
});
// Sign-Up Page
app.route("/signup")
.get(function(req, res) {
res.render("signup");
})
.post(function(req, res) {
User.register({username: req.body.username}, req.body.password, function(err, user) {
if (err) {
console.log(err);
res.redirect("/signup");
} else {
passport.authenticate("local")(req, res, function() {
res.redirect("/secrets");
});
}
});
});
// Sign-In Page
app.route("/signin")
.get(function(req, res) {
res.render("signin");
})
.post(function(req, res) {
// Check database to see if the username provided exists in it.
User.findOne({username: req.body.username}, function(err, foundUser) {
// If username found, create an object "user" to store username and password which was used for login
if (foundUser) {
const user = new User ({
username: req.body.username,
password: req.body.password
});
// Use the "user" object to compare against username and password stored in Database.
// The code below will either return false is no match was found or it will return the 'found user'.
passport.authenticate("local", function (err, user) {
if (err) {
console.log(err);
} else {
// the 'user' below is the user returned from passport.authenticate callback; it will either be false if no match was found or the user
if (user) {
// If the user is found, then he/she is logged in or they are redirected to sign in page.
req.login(user, function(err) {
res.redirect("/secrets");
})
} else {
res.redirect("/signin");
}
}
}) (req, res);
// If no username is found, redirect to sign in page; i.e. user does not exists
} else {
res.redirect("/signin");
}
});
});
// Secrets Page
app.route("/secrets")
.get(function(req, res) {
if (req.isAuthenticated()) {
User.find({secret: {$ne: null}}, function(err, users) {
if (!err) {
if (users) {
res.render("secrets", {usersWithSecrets: users});
} else {
console.log(err);
}
} else {
console.log(err);
}
});
} else {
res.redirect("/signin");
}
});
// Sign-Out Route
app.route("/signout")
.get(function(req, res) {
req.logout(function(err) {
if (err) {
console.log(err);
} else {
res.redirect("/")
}
});
});
// Submit Route
app.route("/submit")
.get(function(req, res) {
if (req.isAuthenticated()) {
res.render("submit");
} else {
res.redirect("/signin");
}
})
.post(function(req, res) {
const submittedSecret = req.body.secret;
if(req.isAuthenticated()) {
req.user.secret.push(submittedSecret);
req.user.save(function() {
res.redirect("/secrets");
})
} else {
res.redirect("/signin");
}
});
// Delete Route
app.route("/delete")
.get(function(req, res) {
if (req.isAuthenticated()) {
res.render("delete", {secrets: req.user.secret});
} else {
res.redirect("/signin");
}
})
.post(function(req, res) {
if(req.isAuthenticated()){
User.findById(req.user.id, function (err,foundUser){
foundUser.secret.splice(foundUser.secret.indexOf(req.body.secret),1);
foundUser.save(function (err) {
if(!err){
res.redirect("/secrets");
}
});
});
}else {
res.redirect("/signin");
}
});
Turns out it wasn't a problem with my MongoDB connection. It was a problem with session storage. I used the 'connect-mongo' (https://www.npmjs.com/package/connect-mongo) package from npm and the web app started working as expected.
All I had to do:
npm install connect-mongo
const MongoStore = require('connect-mongo');
app.use(session({
store: MongoStore.create({mongoUrl: "mongodb://127.0.0.1:27017/databaseName"})
}));

User Authentication not working as expected

I created a web app that authenticates users using Passport.js where they are able to log-in or register either using the web form or google authentication. The web app is working fine and as expected locally, however, when deployed, it is allowing me to register but, when logged in, I try to press any button, it logs me out asks me to sign in again. The log is giving me a bunch of 304 and 302 status code errors. I don't know what is wrong. Please help.
Once I log in, I expect it to keep me logged in until I sign out. Instead, once I log in and try to use the web app, it logs me out. I am using: express, mongoose, session, express-passport, passport-local-mongoose, passport-google-oauth20, mongoose-findorcreate
Here is the link to my deployed web app: https://upset-gilet-lion.cyclic.app/
Here is a screenshot of deployed app logs:
logs screenshot
Here is my app.js code:
require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");
const ejs = require("ejs");
const mongoose = require("mongoose");
const session = require("express-session");
const passport = require("passport");
const passportLocalMongoose = require("passport-local-mongoose");
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const findOrCreate = require("mongoose-findorcreate")
const app = express();
app.use(express.static(__dirname + "/public/"));
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({
extended: true
}));
// Initializing Session
app.use(session({
secret: process.env.SECRET,
resave: false,
saveUninitialized: false
}));
// Initializing Passport
app.use(passport.initialize());
app.use(passport.session());
// Connecting to MongoDB using Mongoose
mongoose.set('strictQuery', true);
const mongodb = "mongodb://127.0.0.1:27017/userDB"
const mongodbNet = "mongodb+srv://admin-bhoami:"+ process.env.PASSWORD +"#secretscluster.uztbzho.mongodb.net/userDB";
mongoose.connect(mongodbNet, function(err){
if (err) {
console.log('Unable to connect to MongoDB');
console.log(err);
} else {
console.log('Successfully connected to MongoDB');
}
});
// Creating a new User Database
const userSchema = new mongoose.Schema ({
email: String,
password: String,
googleId: String,
secret: Array
});
userSchema.plugin(passportLocalMongoose);
userSchema.plugin(findOrCreate);
const User = new mongoose.model("User", userSchema);
// Set Local Login Strategy
passport.use(User.createStrategy());
// Serialize and Deserialize
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
// Google Authentication Strategy that authenticates users using a Google Account and OAuth 2.0 tokens.
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "https://upset-gilet-lion.cyclic.app/auth/google/secrets", //"http://localhost:3000/auth/google/secrets"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({username: profile.emails[0].value, googleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
// Home Page
app.route("/")
.get(function(req, res) {
res.render("home");
});
// Google Authentication Route
app.get('/auth/google',
passport.authenticate('google', { scope: ["openid", "profile", "email"] }));
app.get("/auth/google/secrets",
passport.authenticate("google", { failureRedirect: "/signin" }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect("/secrets");
});
// Sign-Up Page
app.route("/signup")
.get(function(req, res) {
res.render("signup");
})
.post(function(req, res) {
User.register({username: req.body.username}, req.body.password, function(err, user) {
if (err) {
console.log(err);
res.redirect("/signup");
} else {
passport.authenticate("local")(req, res, function() {
res.redirect("/secrets");
});
}
});
});
// Sign-In Page
app.route("/signin")
.get(function(req, res) {
res.render("signin");
})
.post(function(req, res) {
// Check database to see if the username provided exists in it.
User.findOne({username: req.body.username}, function(err, foundUser) {
// If username found, create an object "user" to store username and password which was used for login
if (foundUser) {
const user = new User ({
username: req.body.username,
password: req.body.password
});
// Use the "user" object to compare against username and password stored in Database.
// The code below will either return false is no match was found or it will return the 'found user'.
passport.authenticate("local", function (err, user) {
if (err) {
console.log(err);
} else {
// the 'user' below is the user returned from passport.authenticate callback; it will either be false if no match was found or the user
if (user) {
// If the user is found, then he/she is logged in or they are redirected to sign in page.
req.login(user, function(err) {
res.redirect("/secrets");
})
} else {
res.redirect("/signin");
}
}
}) (req, res);
// If no username is found, redirect to sign in page; i.e. user does not exists
} else {
res.redirect("/signin");
}
});
});
// Secrets Page
app.route("/secrets")
.get(function(req, res) {
if (req.isAuthenticated()) {
User.find({secret: {$ne: null}}, function(err, users) {
if (!err) {
if (users) {
res.render("secrets", {usersWithSecrets: users});
} else {
console.log(err);
}
} else {
console.log(err);
}
});
} else {
res.redirect("/signin");
}
});
// Sign-Out Route
app.route("/signout")
.get(function(req, res) {
req.logout(function(err) {
if (err) {
console.log(err);
} else {
res.redirect("/")
}
});
});
// Submit Route
app.route("/submit")
.get(function(req, res) {
if (req.isAuthenticated()) {
res.render("submit");
} else {
res.redirect("/signin");
}
})
.post(function(req, res) {
const submittedSecret = req.body.secret;
if(req.isAuthenticated()) {
req.user.secret.push(submittedSecret);
req.user.save(function() {
res.redirect("/secrets");
})
} else {
res.redirect("/signin");
}
});
// Delete Route
app.route("/delete")
.get(function(req, res) {
if (req.isAuthenticated()) {
res.render("delete", {secrets: req.user.secret});
} else {
res.redirect("/signin");
}
})
.post(function(req, res) {
if(req.isAuthenticated()){
User.findById(req.user.id, function (err,foundUser){
foundUser.secret.splice(foundUser.secret.indexOf(req.body.secret),1);
foundUser.save(function (err) {
if(!err){
res.redirect("/secrets");
}
});
});
}else {
res.redirect("/signin");
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, function() {
console.log("Server started on Port ${PORT}");
});

Error: TOO_MANY_REDIRECTS from localhost using express in node.js

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();
}
});

Can't send headers after they are sent

I am new to nodejs. I have been trying to create a simple login and register system using mongodb and express. I have created the entire app but with one error:
var express = require('express')
, passport = require('passport')
, util = require('util')
, LocalStrategy = require('passport-local').Strategy;
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var session = require('express-session');
//var flash = require('connect-flash');
var monk = require('monk');
var db = monk('localhost:27017/loginsystem');
var collection = db.get('messagescollection');
function findById(id, fn) {
var collection = db.get('messagescollection');
collection.findOne({ _id: id }).on('success', function (doc) {
fn(null, doc);
});
}
function findByUsername(username, fn) {
collection.findOne({ username: username }).on('success', function(doc) {
return fn(null, doc);
});
return fn(null, null);
}
var app = express();
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
app.use(function(req,res,next){
req.db = db;
next();
});
app.use(cookieParser());
app.use(session({
secret: 'keyboard cat'
hours
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
console.log("user",user);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
console.log(id,"id");
findById(id, function (err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy(
function(username, password, done) {
findByUsername(username, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown user ' + username }); }
if (user.password != password) { return done(null, false, { message: 'Invalid password' }); }
return done(null, user);
})
}
));
app.use(express.static(__dirname + '/public'));
app.get('/account', ensureAuthenticated, function(req, res){
res.send(req.user);
});
app.get('/login', function(req, res){
res.redirect("/login.html") //redirect back to homepage
});
app.get('/register', function(req, res) {
res.redirect("/register.html") //redirect back to homepage
})
app.post('/login', passport.authenticate('local', { failureRedirect: '/login'}), function(req, res) {
console.log("success",req.user);
res.redirect('/account');
});
app.post('/register', function(req, res) {
console.log(req.body);
// Submit to the DB
collection.insert({
"username" : req.body.username,
"email" : req.body.email,
"password" : req.body.password
}, function (err, doc) {
if (err) {
// If it failed, return error
res.send("There was a problem adding the information to the database.");
}
else {
//res.redirect('/account');
}
});
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
app.listen(9000);
function ensureAuthenticated(req, res, next) {
console.log("req.user",req.user,req.session);
if (req.isAuthenticated()) { return next(); }
res.redirect('/login');
}
When I register, the user is added to the database. And it is redirected to /login but when I login then I get this error:
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
at ServerResponse.header (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/express/lib/response.js:700:10)
at ServerResponse.res.location (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/express/lib/response.js:814:8)
at ServerResponse.redirect (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/express/lib/response.js:853:8)
at /home/mareebsiddiqui/SummerOfCode/loginsystem/app.js:130:7
at Layer.handle [as handle_request] (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/express/lib/router/layer.js:82:5)
at next (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/express/lib/router/route.js:110:13)
at complete (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/passport/lib/middleware/authenticate.js:243:13)
at /home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/passport/lib/middleware/authenticate.js:250:15
at pass (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/passport/lib/authenticator.js:427:14)
How could I resolve this issue? Thanks.
EDIT: I know that there are many questions with the same title but there problems are different. This error is caused by many different situations and my situation is different from others. I have a problem of redirects whereas other questions have problems of ending a response.
You should use return response.<method>. For example, return response.redirect().
Also, response.end() might be helpful.

How can I prevent the browser's back button from accessing restricted information, after the user has logged out?

I'm Using this example found on github for passport local strategy with mongoose/mongodb.
The problem that I'm encountering is that when a user logs out, they can still access restricted information by hitting the back button on the browser. I'm kind of new to node.js but I would imagine that some kind of hook would need to be implemented to call the ensureAuthenticated function - located all the way at the very bottom of the code - before the back and forward buttons are executed. How can I prevent a user from accessing restricted information, by hitting the back button, after the user has logged out?
var express = require('express')
, passport = require('passport')
, LocalStrategy = require('passport-local').Strategy
, mongodb = require('mongodb')
, mongoose = require('mongoose')
, bcrypt = require('bcrypt')
, SALT_WORK_FACTOR = 10;
mongoose.connect('localhost', 'test');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback() {
console.log('Connected to DB');
});
// User Schema
var userSchema = mongoose.Schema({
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true},
accessToken: { type: String } // Used for Remember Me
});
// Bcrypt middleware
userSchema.pre('save', function(next) {
var user = this;
if(!user.isModified('password')) return next();
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if(err) return next(err);
bcrypt.hash(user.password, salt, function(err, hash) {
if(err) return next(err);
user.password = hash;
next();
});
});
});
// Password verification
userSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if(err) return cb(err);
cb(null, isMatch);
});
};
// Remember Me implementation helper method
userSchema.methods.generateRandomToken = function () {
var user = this,
chars = "_!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
token = new Date().getTime() + '_';
for ( var x = 0; x < 16; x++ ) {
var i = Math.floor( Math.random() * 62 );
token += chars.charAt( i );
}
return token;
};
// Seed a user
var User = mongoose.model('User', userSchema);
// var usr = new User({ username: 'bob', email: 'bob#example.com', password: 'secret' });
// usr.save(function(err) {
// if(err) {
// console.log(err);
// } else {
// console.log('user: ' + usr.username + " saved.");
// }
// });
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
//
// Both serializer and deserializer edited for Remember Me functionality
passport.serializeUser(function(user, done) {
var createAccessToken = function () {
var token = user.generateRandomToken();
User.findOne( { accessToken: token }, function (err, existingUser) {
if (err) { return done( err ); }
if (existingUser) {
createAccessToken(); // Run the function again - the token has to be unique!
} else {
user.set('accessToken', token);
user.save( function (err) {
if (err) return done(err);
return done(null, user.get('accessToken'));
})
}
});
};
if ( user._id ) {
createAccessToken();
}
});
passport.deserializeUser(function(token, done) {
User.findOne( {accessToken: token } , function (err, user) {
done(err, user);
});
});
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a username and password), and invoke a callback
// with a user object. In the real world, this would query a database;
// however, in this example we are using a baked-in set of users.
passport.use(new LocalStrategy(function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown user ' + username }); }
user.comparePassword(password, function(err, isMatch) {
if (err) return done(err);
if(isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Invalid password' });
}
});
});
}));
var app = express();
// configure Express
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.engine('ejs', require('ejs-locals'));
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({ secret: 'keyboard cat' })); // CHANGE THIS SECRET!
// Remember Me middleware
app.use( function (req, res, next) {
if ( req.method == 'POST' && req.url == '/login' ) {
if ( req.body.rememberme ) {
req.session.cookie.maxAge = 2592000000; // 30*24*60*60*1000 Rememeber 'me' for 30 days
} else {
req.session.cookie.expires = false;
}
}
next();
});
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/../../public'));
});
app.get('/users', function(req, res) {
var users = User.find();
console.log(users);
res.send(users);
});
app.get('/', function(req, res){
res.render('index', { user: req.user });
});
app.get('/account', ensureAuthenticated, function(req, res){
res.render('account', { user: req.user });
});
app.get('/login', function(req, res){
res.render('login', { user: req.user, message: req.session.messages });
});
// POST /login
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
//
// curl -v -d "username=bob&password=secret" http://127.0.0.1:3000/login
//
/***** This version has a problem with flash messages
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login', failureFlash: true }),
function(req, res) {
res.redirect('/');
});
*/
// POST /login
// This is an alternative implementation that uses a custom callback to
// acheive the same functionality.
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
req.session.messages = [info.message];
return res.redirect('/login')
}
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/');
});
})(req, res, next);
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
app.listen(3000, function() {
console.log('Express server listening on port 3000');
});
// Simple route middleware to ensure user is authenticated.
// Use this route middleware on any resource that needs to be protected. If
// the request is authenticated (typically via a persistent login session),
// the request will proceed. Otherwise, the user will be redirected to the
// login page.
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
Edit
I think I might be on to something but can't get it to work. After doing some more research,
It seems that what I need to do is prevent local cacheing. I'm attempting to do this from within my app.configure function:
app.configure(function() {
app.use(function(req, res, next) {
res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');
next();
});
});
However, this does not seem to be effecting my headers.
Since the browser pulls that page from cache, it doesn't matter what you do on that page, unless you add a JS check to see if the user is still authenticated... but that doesn't solve the problem of the page being in cache.
Reframing the problem as a cache one, I found this answer: https://stackoverflow.com/a/24591864/217374
It's been over a year since you asked, so I don't expect you specifically to need the answer any more, but there it is for anyone else who comes by.
When the user navigates back in the browser, the data is shown from the local browser cache, and not requested from your server. What you can do, is to add some javascript to your logout event. That piece of js can remove sensitive pages from the browser history.
You can work with window.history to manipulate the browser history.
Have a look in this guide for manipulating the browser history and the window.history api .
Not sure if this is bulletproof.
Add these lines in your html (or view files)
meta(http-equiv='Cache-Control', content='no-store, no-cache, must-revalidate')
meta(http-equiv='Pragma', content='no-cache')
meta(http-equiv='Expires', content='-1')

Resources