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}");
});
Related
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"})
}));
I am trying to use the register function from passport-local-mongoose but for some reason I am unable to make it work. I am very new to web development in general and mostly don't understand exactly what these libraries and frameworks are doing behind the scenes. I understand that passport-local-mongoose is suppose to make registering users easier but for some reason this code is not working. I have put the relevant code here, if anyone needs more code then please tell me.
const userSchema = new mongoose.Schema({
email: String,
password: String
});
const User = new mongoose.model("User", userSchema);
app.post("/register", function(req, res) {
User.register({username:
req.body.userName
}, req.body.password, function(err, user) {
if(err) {
console.log(err);
res.redirect("/register");
} else {
passport.authenticate("local")(req, res, function() {
res.redirect("/secrets");
});
}
});
});
I think there might be some problem in the way you imported the passport-local-mongoose file.
//jshint esversion:6
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();
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)
passport.use(User.createStrategy());
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "http://shielded-ocean-
16775.herokuapp.com/auth/google/secrets",
userProfileUrl: "https://www.googleleapis.com/oauth2/v3/userinfo"
},
function(accessToken, refreshToken, profile, cb) {
console.log(profile);
User.findOrCreate({
googleId: profile.id
}, function(err, user) {
return cb(err, user);
});
}
));
app.use(express.static("public"));
app.use(bodyParser.urlencoded({
extended: true
}));
app.set("view engine", "ejs");
app.use(session({
secret: "The Secret Shall Never Be Disclosed",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session())
mongoose.connect("mongodb+srv://admin-videep:805122#cluster1805.nuxh2.mongodb.net/userDB", {
useNewUrlParser: true
})
mongoose.set("useCreateIndex", true);
app.get("/", function(req, res) {
res.render("home");
})
app.get("/auth/google",
passport.authenticate("google", {
scope: ["profile"]
})
)
app.get("/auth/google/secrets",
passport.authenticate('google', {
failureRedirect: "/login"
}),
function(req, res) {
res.redirect("/secrets");
});
app.get("/login", function(req, res) {
res.render("login");
})
app.get("/register", function(req, res) {
res.render("register");
})
app.get("/secrets", function(req, res) {
User.find({
"secret": {
$ne: null
}
}, function(err, foundUsers) {
if (err) {
console.log(err);
} else {
if (foundUsers) {
res.render("secrets", {
usersWithSecrets: foundUsers
})
}
}
})
})
app.get("/submit", function(req, res) {
if (req.isAuthenticated()) {
res.render("submit");
} else {
res.redirect("/login")
}
})
app.get("/logout", function(req, res) {
req.logout()
res.redirect("/")
})
app.get("/about", function(req, res){
res.render("about")
})
app.post("/register", function(req, res) {
User.register({
username: req.body.username
}, req.body.password, function(err, user) {
if (err) {
console.log(err);
res.redirect("/register");
} else {
passport.authenticate("local")(req, res, function() {
res.redirect("/secrets")
})
}
})
});
app.post("/login", function(req, res) {
const user = new User({
username: req.body.username,
password: req.body.password
})
req.login(user, function(err) {
if (err) {
console.log(err);
} else {
passport.authenticate("local")(req, res, function() {
res.redirect("/secrets")
})
}
})
})
app.post("/submit", function(req, res) {
const submittedSecret = req.body.secret;
if (req.isAuthenticated()) {}
User.findById(req.user.id, function(err, foundUser) {
if (err) {
console.log(err);
res.redirect("/login")
} else {
if (foundUser) {
foundUser.secret.push(submittedSecret)
foundUser.save()
res.redirect("/secrets")
}
}
})
})
let port = process.env.PORT;
if (port == null || port == "") {
port = 3000;
}
app.listen(port)
Here is my full code file and it works perfectly well
I am trying to use postman for the first time and yes i am a beginner with this authentication. I have a register page and a login page in angular and my backend is running on another port which is coded in nodejs. So i have installed passport and other required packages for the login authentication and registering user process. But on sending request to register route for registering a user i get the UnhandledPromiseRejectionWarning error. Even though there is then and catch inside the route it is still giving this error because of which i am confused. The data that i am sending is perfect but the error is occuring on the server side.
Here is my app.js file that contains all the routes
var express = require('express');
var mongoose = require('mongoose');
var bodyparser = require('body-parser');
var cors = require('cors');
var session = require('cookie-session');
var flash = require('connect-flash');
var passport = require('passport');
var bcrypt = require('bcryptjs');
require('./config/passport')(passport);
var User = require('./models/User');
var app = express();
app.use(bodyparser.json());
app.use(cors());
var expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(session({
name: 'session',
keys: ['key1', 'key2'],
cookie: {
secure: true,
httpOnly: true,
domain: 'example.com',
path: 'foo/bar',
expires: expiryDate
}
}))
app.set('port', process.env.port || 3000);
app.use(passport.initialize());
app.use(passport.session());
// Connect flash
app.use(flash());
// Global variables
app.use(function(req, res, next) {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
res.locals.error = req.flash('error');
next();
});
var db = mongoose.connect("mongodb://localhost:27017/server", {
useNewUrlParser: true
}, function(err, response) {
if (err) {
console.log('There is error in connecting with mongodb');
}
console.log('Connection has been established.');
});
app.get('/', (req, res) => {
res.send("hello");
});
//Trying registering with passport
app.post('/register', (req, res) => {
console.log(req.body);
debugger;
const { firstname, lastname, email, password } = req.body;
let errors = [];
if (errors.length > 0) {
res.render('register', {
errors,
name,
email,
password,
password2
});
} else {
User.findOne({ email: email }).then(user => {
if (user) {
errors.push({ msg: 'Email already exists' });
res.render('register', {
errors,
firstname,
lastname,
email,
password
});
} else {
const newUser = new User({
firstname,
lastname,
email,
password
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save()
.then(user => {
req.flash(
'success_msg',
'You are now registered and can log in'
);
res.redirect('/login');
})
.catch(err => console.log(err));
});
});
}
});
}
});
//end
app.post('/login', (req, res, next) => {
console.log(req.body);
passport.authenticate('local', {
successRedirect: '/dashboard',
failureRedirect: '/login',
failureFlash: true
})(req, res, next);
});
app.listen(app.get('port'), function(err, response) {
console.log("Server is running");
});
passport.js file in config folder
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
// Load User model
const User = require('../models/User');
module.exports = function(passport) {
passport.use(
new LocalStrategy({ emailField: 'email' }, (email, password, done) => {
// Match user
User.findOne({
email: email
}).then(user => {
if (!user) {
return done(null, false, { message: 'That email is not registered' });
}
// Match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Password incorrect' });
}
});
});
})
);
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
};
To resolve this specific issue, you need to provide a catch() when you execute User.findOne() at the same level as the outermost then():
User.findOne({ email: email })
.then(user => { /* existing code */})
.catch(err => console.log(err)); // add catch()
And:
// Match user
User.findOne({ email: email })
.then(user => { /* existing code */ })
.catch(err => console.log(err)); // add catch()
Regarding the issue with the engine. You need to specify a rendering engine such as pug or html or ejs, this is something that Express as a framework expects. The link you provided that you are basing this code off of is using a rendering engine, specifically ejs. This is evident from the use of res.render(), which renders templates from the "views" folder.
app.set('view engine', 'ejs');
Even with the rendering engine, set you don't actually need to render any templates. As long as you remove res.render() and instead just use res.send() or equivalent, you can use this Express application as an API.
If your intention is to NOT use templates but still render the built angular files, you need to update the code and remove all instances of res.render() and res.redirect(). You can instead, still setting a view engine to satisfy Express, do something along the following:
// towards top of file
app.use(express.static(path.join(__dirname, 'path/to/built/angular')));
// ...
// after all other routes
app.get('*', (req, res) =>{
res.sendFile(path.join(__dirname+'/path/to/built/angular/index.html'));
});
Hopefully that helps!
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;
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')