Passport Local strategy session using express in Node.js - javascript

I'm a newbie in Node.js and used passport-local strategy for login session. I already set up all things in my web app, but I'm having a big problem with session.
I tried multiple users can login to my web app at the same time, but there is a problem. One of users(ex. username is Paul) log in first, and then another user(Chris) is log in after, then Paul's view of my web app is changed to Chris' view.
I'm not sure you guys understand what I mean by that but here is one of my code in Node.js
app.get('/location', isLoggedIn, function(req, res){
var user_temp = {user: ''};
user_temp.user = global_username;
res.render('location/index1', user_temp);
});
And below code is 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;
username_tmp = email_address;
var username = email_address.substring(0, email_address.lastIndexOf("#"));
global_username = username;
pass = req.body.password;
return done(null, user);
});
}
)
);
I think the problem is because of global_username, so Paul can see Chris' view of location. However, I don't know how to get the user variable and put that into ejs for each users.
Can anybody help me out here....?
Thanks in advance!
P.S. If what I asked is confused,
I can edit my question again and explain more...

You can't use globals, they get overwritten for each request which will result in exactly the issue that you're describing.
Instead, if you have set up Passport and express-session correctly, Passport will provide the user data for each (authenticated) request through req.user:
app.get('/location', isLoggedIn, function(req, res) {
res.render('location/index1', { user : req.user });
});
You can also take a look at the passport-local example application to get an idea on how everything is connected.

Related

How to do user login correctly with nodejs and express

I am very new to web development, and have been using Google as a guide.
If I put a wrong login that does not match what I have in my database, the website just gets stuck and keeps trying to “load”. I also am confused on how to do token-based authentication for login and would love some more guidance on that, the guide I am following talks about database encryption and OAuth 2.0 with Google.
If the user logs in with a username and password that is not correct, I just want it to give an error and reload back to login.ejs.
Thank you for any help!
The issue might be you are not returning anything when foundUser is null or if the password doesn’t match.
If there is any error you can redirect it to the /login route with query param (err) which can be read by the client using JS at page load. If there is a nonempty query param err then read it and show it in some popup.
res.redirect("/login?err=The username does not exist");
//connect to mongodb
mongoose.connect("mongodb://localhost:27017/userDB", {
useNewUrlParser: true
});
const userSchema = {
username: String,
password: String
};
const User = new mongoose.model("User", userSchema);
app.get("/", function(req, res) {
res.render("home"); //render home.ejs
});
app.get("/login", function(req, res) {
res.render("login"); //render login.ejs
});
app.post("/login", function(req, res) {
const username = req.body.username;
const password = req.body.password;
try {
User.findOne({
username: username
}, function(err, foundUser) {
if (err || !foundUser) {
return res.redirect("/login?err=The username does not exist");
}
if (foundUser.password !== password) {
// you can use bcryptjs for hashing and comparing hashed values.
return res.redirect("/login?err=The username or password is invalid");
}
res.redirect("/counter");
});
} catch (error) {
res.redirect("/login?err=something went wrong!");
}
});
You can read more about handling authentication in nodeJS here. also check passportjs

How to call deserializeUser with passport

I found the following post on how deserializeUser is supposed to work: PassportJS serializeUser and deserializeUser execution flow
However When I try and send a JSON to the server which contains the key for the user, I cannot call req.user to find the user's details.
I am unsure on how to check what is going on because a lot of stuff is going on under the hood.
Does passport expect that I send a cookie to express containing the key? Any specific name for the cookie or format?
Does it require that I sent the key in JSON format?
const express = require('express')
const app = express()
const mongoose = require('mongoose')
var cors = require('cors')
const bodyParser = require('body-parser')
var http = require('http')
var cookieParser = require('cookie-parser');
app.use(cookieParser('someSecret'));
var session = require('express-session');
app.use(session());
var flash=require("connect-flash");
app.use(flash());
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
session: true,
passReqToCallback: true
},
function(req, username, password, done){
registrationModel.findOne({username: username}, function(err, user){
if (err) { return done(err); }
if (!user){
console.log("Username incorrect")
return done(null, false, { message: 'Incorrect username.'});
}
if (user.password != password){
console.log("Password Incorrect")
return done(null, false, { message: 'Incorrect Password.'});
} else {
console.log("Returning good stuff")
console.log(user)
return done(null, user);
}
});
}
));
//Needed for authenticating the session and initializing passport
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
console.log("Serializing User")
console.log(user.id)
console.log("Should be serialized")
done(null, user.id);
});
passport.deserializeUser(function(id, done){
users.User.findById(id, function(err, User) {
console.log('attempting to deserialize user')
console.log(user)
console.log('--------------')
if (User){
done(err, User);
} else {
done(err, null);
}
})
})
app.post('/api/authenticate', passport.authenticate('local'),
function(req, res){
const individualIWant = jsonQuery('req.sessionStore.sessions')
res.cookie('FreeUp', req.session.passport.user)
var cookie = JSON.stringify(req.session.passport.user)
res.send({'name': 'FreeUp', 'value': cookie})
})
I can see a cookie in the browser:
name:"FreeUp"
value:""59c4cf4ecb364a000f23a707""
The value is the database ID for the object and not the key for the serialized object. I understand that I need to access the key for serialized object by using res.req.sessionID and to send that back instead.
However the point still stands, I do not know whether to send it back as a json file or a cookie. It seems like such a complex library to use.
Which format does passport expect me to use when sending the data back from Ember?
Thanks!
I had a lot of trouble with this as well.
From what I understand Passport doesn't actually create a session itself. So you need to use another piece of middleware, like express-session, and you'll also need a cookie parser.
I built my authentication using the tutorial they have at Scotch: https://scotch.io/tutorials/easy-node-authentication-setup-and-local. It's really well commented and the only straight forward tutorial I could find (especially for local authentication). I can verify that it worked for me and req.user is storing all the credentials when I pass it in my routes.
Hope that helps.

How to protect route endpoints using Passport?

I am trying to build user authentication into my simple Node.js app using the tutorial here: http://code.tutsplus.com/tutorials/authenticating-nodejs-applications-with-passport--cms-21619
It works great in terms of protecting the application home page so that it can only be accessed after logging in, but I am having a really hard time restricting my REST endpoints to only logged in users. As in using Postman I can still call the end points without any authentication.
In my route I have the following:
var express = require('express');
var router = express.Router();
// if the user is authenticated
var isAuthenticated = function (req, res, next) {
if (req.isAuthenticated())
return next();
res.json("not authenticated");
}
/*
* GET carlist.
*/
router.get('/carlist', isAuthenticated, function(req, res) {
var db = req.db;
var collection = db.get('carlist');
collection.find({},{},function(e,docs){
res.json(docs);
});
});
This doesn't seem to work, even if I actually enter correct credentials I am always returned "not authenticated". What I am I missing here?
EDIT:
Full code here: https://gist.github.com/tudorific/d99bc51cfbd3d9d732a3bb1b93ed7214
Thanks in advance for the help!
I figured it out. Since I was using a LocalStrategy the IsAuthenticated method was looking for the credentials in the session rather than at the Basic Credentials I was sending with Postman. So I needed to create the following new BasicStrategy:
var passport = require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var Employer = require('../models/employer');
var bCrypt = require('bcrypt-nodejs');
passport.use(new BasicStrategy(
function(username, password, done) {
Employer.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
//if (!user.validPassword(password)) { return done(null, false); }
if (!isValidPassword(user, password)){ return done(null, false); }
return done(null, user);
});
var isValidPassword = function(employer, password){
return bCrypt.compareSync(password, employer.password);
}
}));
And then use that strategy in my route like this:
router.get('/carlist', passport.authenticate('basic', function(req, res) {
var db = req.db;
var collection = db.get('cars');
collection.find({},{},function(e,docs){
res.json(docs);
});
});
This would use my basic authentication credentials from Postman to connect to the website.
Thanks to Neta Meta's advice in the comments to my OP I was able to arrive to this result and a bit more reading on the Passport documentation to understand the differences between the strategies.

req.session.passport is empty: req.user undefined

I've asked a similar question before, but I noticed it was in the Javascript section.
I have more specific ideas of what might be going wrong now, as well.
Basically, req.session.passport is empty in my logs. Whenever I start navigating around my site, req.user becomes undefined because the session doesn't have Passport's logged in user anymore.
I would like to know if anyone knows how to solve this? Maybe it's just an error in the configuration of Passport, or the entire Express setup?
App.js:
var express = require("express"),
bodyParser = require("body-parser"),
mongodb = require("mongodb"),
mongoose = require("mongoose"),
uriUtil = require("mongodb-uri"),
morgan = require("morgan"),
session = require("express-session"),
passport = require("passport"),
flash = require("connect-flash"),
ip = "hidden",
port = process.env.PORT || 80
var app = express()
app.disable("x-powered-by")
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: true
}))
app.use(morgan("dev")); // log every request to the console
// required for passport
app.use(session({
secret: "hidden",
key: 'asdasdasd',
cookie: { maxAge: 60000, secure: false },
resave: true,
saveUninitialized: false
})); // session secret
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
app.set("view engine", "jade")
app.use(express.static(__dirname + "/views"))
require("./includes/passport")(passport)
require("./includes/subject")
require("./includes/user")
Passport.js:
var LocalStrategy = require("passport-local").Strategy,
User = require("./user"),
bCrypt = require('bcrypt-nodejs')
module.exports = function(passport) {
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user._id);
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
// =========================================================================
// LOCAL SIGNUP ============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called "local"
passport.use('signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : "email",
passwordField : "password",
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
// asynchronous
// User.findOne wont fire unless data is sent back
process.nextTick(function() {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ "email" : email }, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
// check to see if theres already a user with that email
if (user) {
return done(null, false, req.flash("message", "Dit e-mail-adres is al bezet"));
} else {
// if there is no user with that email
// create the user
var newUser = new User();
// set the user's local credentials
newUser.email = email;
newUser.password = createHash(password);
newUser.firstname = req.param('firstname');
newUser.lastname = req.param('surname');
newUser.year = parseInt(req.param('year'));
newUser.study = req.param('study');
newUser.courses = req.param('courses');
newUser.phone = req.param('phone');
newUser.availability = req.param('availability');
newUser.description = req.param('descText');
// save the user
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
});
}));
// =========================================================================
// LOCAL LOGIN =============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'
passport.use("login", new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : "email",
passwordField : "password",
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ "email" : email }, function(err, user) {
// if there are any errors, return the error before anything else
if (err)
return done(err);
// if no user is found, return the message
if (!user) {
console.log('No user found with email ' + email)
return done(null, false, req.flash('message', 'Gebruiker niet gevonden')); // req.flash is the way to set flashdata using connect-flash
}
if (!isValidPassword(user, password)){
console.log('Incorrect Password');
return done(null, false, req.flash('message', 'Onjuist wachtwoord')); // redirect back to login page
}
// all is well, return successful user
return done(null, user);
});
}));
var isValidPassword = function(user, password){
return bCrypt.compareSync(password, user.password);
}
// Generates hash using bCrypt
var createHash = function(password){
return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
}
};
The routes:
api.post("/signup", passport.authenticate("signup", {
successRedirect: "/profile",
failureRedirect: "/",
failureFlash: true
}))
api.post("/login", passport.authenticate("login", {
successRedirect: "/profile",
failureRedirect: "/login"//,
failureFlash: true
}))
router.get("/", function(req, res) {
// serve index.html
res.render("index", {
title: 'Home',
user: req.user,
message: req.flash("message")
})
})
It works on the page that is accessed directly after logging in, which I control as follows:
router.get("/profile", isLoggedIn, function(req, res) {
res.render("profile", {
title: 'Gebruikersprofiel van ' + req.user.firstname + " " + req.user.lastname,
user: req.user // get the user out of session and pass to template
})
})
function isLoggedIn(req, res, next) {
console.log(req.session)
// if user is authenticated in the session, carry on
if (req.isAuthenticated())
return next()
// if they aren't redirect them to the home page
res.redirect("/login")
}
So far, I've tried adding middleware to add req.user to req.session, and doing the same thing in the login POST. Also I've tried changing the order in which I initialize the middleware in app.js. I am using the new express-session version, without CookieParser, as I read that CookieParser is no longer needed.
If anyone can help me in any way, it would be much appreciated! I've been stuck for a while (as have others).
The problem was not anything I did wrong in setting up the session, or Passport in general, but rather in my links.
I read somewhere that someone was accidentally working in multiple domains (his platform was apparently multi-server), and that made me look through my links this morning.
Apparently, I was linking to my website with www. prefixed, but the session was initialized where there was no www. in front of the URL. I saw this in the cookies.
The solution was, therefore, to link through the website consistently, either having www. prefixed everywhere or nowhere.

passportjs authentication using google apps email id

I am trying passport js with google app email id. I am able to authenticate using gmail.com email id. But how can I authenticate if the email id is a google app id (google.com/a/companyname.com).
This is my code
var express = require('express');
var app = express();
var passport = require('passport');
var GoogleStrategy = require('passport-google').Strategy;
passport.use(new GoogleStrategy({
returnURL: 'http://10.3.0.52:3000/auth/google/return',
realm: 'http://10.3.0.52:3000/'
},
function(identifier, profile, done) {
User.findOrCreate({
openId: identifier
}, function(err, user) {
done(err, user);
});
}
));
app.get('/auth/google', passport.authenticate('google'));
app.get('/auth/google/return',
passport.authenticate('google', {
successRedirect: '/',
failureRedirect: '/login'
}));
app.get('/', function(req, res){
res.writeHead(200);
res.end("connected");
});
app.listen(process.env.PORT || 3000);
Your code is missing some vital parts:
...
passport.use(...); // this you have
// these are required as well.
app.use(passport.initialize());
app.use(passport.session());
// please read docs for the following two calls
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
...
With those in place, I can log in using my Google App address just fine.
EDIT: it only works with Node 0.8 though, Node 0.10 gives an error. I think using passport-google-oauth is a better solution anyway. For that, you have to register your application with Google (here); after registration, you'll be supplied both the GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET codes which you can use.
I have created a method that verifies if the email domain is the one i want to authorize:
UserSchema.method('checkFordomain', function(value) {
var parts = value.split('#');
return (parts[1] == 'companyname.com');
});
this is method I put in the model of the user model, using mongoose schema models
if (!user.checkForMMdomain(profile.emails[0].value)) {
return done();
}
in the callback of the passport google strategy
https://github.com/jaredhanson/passport-google-oauth
In your passport.use callback you can perform additional checking based on the domain of the primary email address (or whatever you are checking):
if (profile.emails[0].split('#')[1] !== authorizedDomain) {
return done(null, false);
}

Categories