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.
Related
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.
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.
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.
I got below express node.js server code using Passport. At it my whole routes definition depends upon a MongoDB connection using mongo-db but model used by Passport is done through another connection by mongoose. I mention these two details cause I think it should also be coded in a better way.
However, the main problem is that even though Passport it's doing it's work, I still can go to localhost/registro directly no matter I didn't logged in first.
When someone tried to access to localhost/registro it should be redirected to start page if a login and authentication wasn't done first.
I care about a safe implementation of it, I'd also like to have some information about the user during the session time.
I'm quite confused about what I should try, cookies, sessions, etc. Apart that in new express version middlewares work different than before.
This is my server.js:
var express = require('express')
var mongodb = require('mongodb')
var mongoose = require('mongoose')
var bodyParser = require('body-parser')
var passport = require('passport')
var LocalStrategy = require('passport-local').Strategy;
var app = express()
var BSON = mongodb.BSONPure
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(__dirname+"/public"))
app.use(bodyParser())
var MongoDBClient = mongodb.MongoClient
mongoose.connect('mongodb://localhost/psicologosTuxtepecDB')
var Schema = mongoose.Schema
var userCredential = new Schema({
username: String,
password: String
}, {
collection: 'members'
})
var userCredentials = mongoose.model('members', userCredential)
passport.serializeUser(function(user, done) {
done(null, user);
})
passport.deserializeUser(function(user, done) {
done(null, user);
})
passport.use(new LocalStrategy(function(username, password, done) {
process.nextTick(function() {
userCredentials.findOne({
'username': username,
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false);
}
if (user.password != password) {
return done(null, false);
}
return done(null, user);
});
});
}));
MongoDBClient.connect("mongodb://localhost/psicologosTuxtepecDB", function (error, psicologosTuxtepecDB) {
if (error) {
console.log("We've got a connection error, so far we should take this function better for a correct debug")
}
else {
console.log("Connection to psicologosTuxtepecDB has been successful")
// Seleccionamos una colección
var psicologosCollection = psicologosTuxtepecDB.collection("psicologos")
app.get('/registro', function(request,response) {
response.sendfile("public/html/registro.html")
})
// Cuando nos hagan una petición HTTP de tipo POST en la ruta psicologos...
app.post("/psychos", function(request, response) {
var psychologist = {
personalData: request.body._personalData,
professionalData: request.body._professionalData,
professionalInterests: request.body._professionalInterests
}
psicologosCollection.insert(psychologist, function(error, responseFromDB) {
if (error) {response.send(responseFromDB)}
console.log("Se ha insertado: "+ JSON.strinfigy(responseFromDB))
response.send(responseFromDB)
})
})
app.get("/psychos/:id", function(request, response) {
var id = new BSON.ObjectID(peticion.params.id)
psicologosCollection.findOne(
{'_id':id},
function(error,responseFromDB) { if (error) {response.send(responseFromDB)} response.send(responseFromDB)}
)
})
app.get("/psychos", function(request,response) {
psicologosCollection.find().toArray(function(error,responseFromDB) {
if (error) {response.send(responseFromDB)}
response.send(responseFromDB)
})
})
app.post('/login',
passport.authenticate('local', {
successRedirect: '/loginSuccess',
failureRedirect: '/loginFailure'
})
)
app.get('/loginFailure', function(req, res, next) {
res.redirect('/')
})
app.get('registro', function(request, response) {
response.sendfile('public/html/registro.html')
})
app.get('/loginSuccess', function(req, res, next) {
res.redirect('/registro')
})
app.listen(80, function () {
console.log("app escuchando en el puerto Maricela fecha de nacimiento DDMM")
})
}
})
These are my Passport statements:
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
done(null, user);
})
passport.deserializeUser(function(user, done) {
done(null, user);
})
passport.use(new LocalStrategy(function(username, password, done) {
process.nextTick(function() {
userCredentials.findOne({
'username': username,
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false);
}
if (user.password != password) {
return done(null, false);
}
return done(null, user);
});
});
}));
Express "chains" route methods. The basic idea behind securing routes in Express.js is to have a method that checks the a&a before allowing the request to proceed to the intended route. There are a few ways to do this:
Method 1: Add the auth method to the route declaration
function requireAuth(req,res,next){
if user is authenticated
next();
else
res.send(401);
}
app.get('/registro', requireAuth, function(request, response) {
response.sendfile('public/html/registro.html')
})
Method 2: Declare a route handler for your auth
app.get('/registro', function(req,res,next){
if user is authenticated
next();
else
res.send(401);
})
app.get('/registro', function(request, response) {
response.sendfile('public/html/registro.html')
})
Method 3: Use app.use() instead of a verb
With this method, you need to consider when app.router gets inserted into the middle-ware.
Edit 1: Where would the require auth method be declared
If you plan on placing route handlers in multiple .js files, it's a good idea to put your require auth mehtod in a separate .js file as well and require it where appropriate.
Otherwise, you can just stick it in the same file with everything else.
Edit 2: How do sessions work in Express.js and Passport.js
From the Passport.js documentation, you first need to configure the express session before the passport session:
app.use(express.session({ secret: 'keyboard cat' }));
app.use(passport.initialize());
app.use(passport.session());
Note: You should probably consider using something other than the memory store for session management.
Along with the serializeUser and deserializeUser methods, at this point Passport will have placed a .user on the request.
You can also use req.isAuthenticated() to determine if the user is authenticated.
Note 2: I've had problems getting the serializeUser and deserializeUser methods to work with Passport-Saml. If that is the case, just manage the session yourself.
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);
}