Yes, I see that this question has been asked here and here, but the answers point to either making changes to your deserializeUser callback, or changing something in your Mongoose model.
I've tried the first to no avail and am using the regular ol' NodeJS driver, so I'm not quite sure where to pinpoint the root cause of my issue.
Here's my script:
AUTHENTICATE.JS:
var app = require('express');
var router = app.Router();
var assert = require('assert');
var bcrypt = require('bcrypt');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
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 LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
function(username, password, done) {
User.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.' });
}
return done(null, user);
});
}
));
router.post('/login',
passport.authenticate('local', {successRedirect:'/',
failureRedirect:'/login',failureFlash: false}),
function(req, res) {
res.redirect('/');
});
For my app.js file I've tried a number of the fixes suggested in similar questions but nothing has made a difference. This is what it looks like:
APP.JS:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var expressValidator = require('express-validator');
var passport = require('passport');
var localStrategy = require('passport-local').Strategy;
var mongo = require('mongodb').MongoClient;
var session = require('express-session');
var bcrypt = require('bcrypt');
// Express Session
app.use(session({
secret: 'keyboard cat',
saveUninitialized: true,
resave: true,
cookie: {secure: true}
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
//initialize passport for app
app.use(passport.initialize());
app.use(passport.session());
// make mongodb available to the application
app.use((req, res, next) => {
mongo.connect('mongodb://localhost:27017/formulas', (e, db) => {
if (e) return next(e);
req.db = db;
next();
});
});
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');
app.use(express.static(__dirname + '/public'));
// Express Validator
app.use(expressValidator({
errorFormatter: function(param, msg, value) {
var namespace = param.split('.')
, root = namespace.shift()
, formParam = root;
while(namespace.length) {
formParam += '[' + namespace.shift() + ']';
}
return {
param : formParam,
msg : msg,
value : value
};
}
}));
//define routes here
app.set('port', (process.env.PORT || 3000));
app.listen(app.get('port'), function(){
console.log("The server is now listening on port "+app.get('port'));
});
module.exports = app;
Any help would be greatly appreciated, thank you.
As both questions you included point out, you need to define the User class first before you can use it. It's not something that passportjs or expressjs provide, you need to implement it for yourself or use another module that gives you that functionality (like mongoose).
In the first SO link you shared the answer suggested the OP implement a user model (User) in mongoose (it seems to be a popular choice).
The second links answer simplified things a bit by adding a hard-coded object to represent the user in the deserializer function.
Related
I am new to web development and NodeJS. I am working on authentication using passport. It was a small app so I put check on each route request to check whether the user is authenticated or not; But I guess that technique won't be feasible for a big app.
I want to authenticate the whole app. I know that it has something to do with middleware as each request passes through middleware, but I can't figure out where. Any middleware related explanation would be appreciated.
Here is my code
const express = require('express');
const app = express();
const path = require('path');
const mongo = require('mongodb').MongoClient;
const cookieParser = require('cookie-parser');
const expressHandlebars = require('express-handlebars');
const expressValidator = require('express-validator');
const session = require('express-session');
const passport = require('passport');
const localStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const User = require('./models/user');
const Admin = require('./models/admin');
app.engine('handlebars', expressHandlebars({defaultLayout:'layout'}));
app.set('view engine', 'handlebars');
const port = 8888;
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(session({
secret: 'secret',
saveUninitialized: true,
resave: true
}));
app.use(passport.initialize());
app.use(passport.session());
//express Validator
app.use(expressValidator({
errorFormatter: function(param, msg, value) {
var namespace = param.split('.'),
root = namespace.shift(),
formParam = root;
while (namespace.length) {
formParam += '[' + namespace.shift() + ']';
}
return {
param: formParam,
msg: msg,
value: value
};
}
}));
app.use(express.static(__dirname));
mongoose.Promise = global.Promise;
const url = 'mongodb://localhost:27017/userDB';
mongoose.connect(url);
Here is my login functionality.
app.get("/login", function (req, res) {
const loginPath = path.join(__dirname, '/login.html');
res.sendFile(loginPath);
});
passport.use(new localStrategy({
usernameField: 'adminUsername',
passwordField: 'password',
session: false
},
function (adminUsername, password, done) {
Admin.getAdminByAdminUsername(adminUsername, function (err, admin) {
if (err) throw err;
console.log('getAdmin called');
if (!admin) {
console.log('Admin Not Found');
return done(null, false);
}
Admin.comparePassword(password, admin.password, function (err, isMatch) {
console.log('comparePassword called');
if (err) throw err;
if (isMatch) {
return done(null, admin);
} else {
console.log('Wrong Password!');
return done(null, false);
}
});
});
}));
passport.serializeUser(function (admin, done) {
done(null, admin.id);
});
passport.deserializeUser(function (id, done) {
Admin.getAdminById(id, function (err, admin) {
done(err, admin);
console.log('findById called');
});
});
app.post('/login', passport.authenticate('local', {
failureRedirect: '/login'}), function(req, res){
console.log('login called');
res.redirect('/');
});
function ensureAuthenticated(req, res, next){
console.log(req.isAuthenticated());
if (req.isAuthenticated()) {
return next();
} else {
res.redirect('/login');
}
}
Previously, this is how I put a check on each request. Here is an example.
app.get("/update", ensureAuthenticated, function (req, res) {
const updatePath = path.join(__dirname, '/update.html');
res.sendFile(updatePath);
});
ensureAuthenticated is a middleware that you can also use standalone, like this:
app.use(ensureAuthenticated);
Express will pass requests to middleware and route handlers in order of their declaration. That means that if you add the line above in front of other middleware and route handlers, it will always be called, for each request (but read below why you might not actually want that). That way, you don't have to add it explicitly to each request handler.
Typically though, you separate "requests that require authentication" from requests that don't. For instance, requests for static resources (client-side JS, CSS, HTML, etc) typically don't require authentication. That means that you need to declare the static handler before ensureAuthenticated:
app.use(express.static(__dirname));
app.use(ensureAuthenticated);
The same goes for requests that are part of the login process: the login page and login request handler, for the simple reason that requiring a user to be logged in before they can access the login page doesn't make sense.
So the overall structure of your middleware/route handlers would look like this:
Common middleware (body parser, cookie handler, passport middleware, etc)
app.use(express.static(...))
Login routes
app.use(ensureAuthenticated)
"Protected" routes
I have a project using passportjs fully working, but not showing flash messages.
The passportjs file looks like this:
passport.use('local-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 in the req from our route (lets us check if a user is logged in or not)
},
function (req, email, password, done) {
if (email) {
email = email.toLowerCase()
} // Use lower-case e-mails to avoid case-sensitive e-mail matching
// asynchronous
process.nextTick(function () {
User.findOne({ 'local.email': email }, function (err, user) {
// if there are any errors, return the error
if (err) {
console.log(err)
return done(err)
}
// if no user is found, return the message
if (!user) {
console.log('No user found')
return done(null, false, req.flash('message', 'No user found'))
}
My routes.js file looks like this:
// Login
app.get('/login', function (req, res) {
console.log('login get')
res.render('pages/login', {message: req.flash('messsage')})
})
my login.ejs file looks like this:
<%if (message) {%>
<%=message%>
<%}%>
I have no idea why it isn't working?
Here comes my server.js if it is any problem related to includes:
var express = require('express')
var mongoose = require('mongoose')
var passport = require('passport')
var path = require('path')
var ejs = require('ejs') // Not used?
var fs = require('fs') // Not used?
const session = require('express-session')
const MongoStore = require('connect-mongo')(session)
// Reads .env variables for encrypted data
require('dotenv').config()
var port = process.env.PORT || 8080
var db = process.env.MONGO_URI
mongoose.connect(db) // connect to our database
var app = express()
app.set('views', './views')
app.set('view engine', 'ejs')
// GET /public/style.css etc.
app.use(express.static(path.join(__dirname, '/public')))
var bodyParser = require('body-parser')
app.use(bodyParser.urlencoded())
app.use(session({
secret: 'hakunamatata',
store: new MongoStore({
mongooseConnection: mongoose.connection,
saveUninitialized: false,
resave: false
})
}))
var flash = require('connect-flash')
app.use(flash())
var initPassport = require('./app/authentication/passport')
initPassport(passport)
app.use(passport.initialize())
app.use(passport.session()) // persistent login sessions
require('./app/routes/routes')(app, passport)
app.listen(port)
console.log('Dev port is: ' + port)
I tested with:
req.flash('loginMessage', 'No user found')
instead of:
req.flash('message', 'No user found')
and on the routes.js file:
{message: req.flash('loginMessage')}
instead of:
{message: req.flash('message')}
And now it works... I dont know what made the difference, but I hope this can help someone else in the future..
I'm loosely following the tutorial here, and I'm not sure why it's not working. Logging in will work fine -- it accepts and rejects me exactly how I'd expect, it will just not save the session, so visiting any links after will say that I am not authenticated.
Vivaldi dev panel says that no cookies or sessions are saved. I have set it up to save my sessions in redis and user authentication in PostgreSQL, and both databases are connected to fine.
Here is the relevant code:
index.js:
const express = require('express');
const passport = require('passport');
const session = require('express-session');
const Redis = require('ioredis');
const RedisStore = require('connect-redis')(session);
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const auth = require("./app/authenticate");
const config = require('./config');
const app = express();
var redis_client = new Redis({
host: config.redisStore.host,
port: config.redisStore.port,
lazyConnect: true
});
redis_client.connect().catch(function(err) {
throw err;
});
app.use(cookieParser(config.redisStore.secret));
app.use(session({
cookie : {
maxAge: 36000000000,
secure: true
},
secret: config.redisStore.secret,
store: new RedisStore({ client: redis_client }),
resave: false,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.urlencoded({extended: true}));
auth.init.initPassport();
app.post('/login', passport.authenticate('local', {
successRedirect: '/testroute',
failureRedirect: '/'
}));
var urlencodedParser = bodyParser.urlencoded({extended: false});
app.get("/testroute", passport.authenticationMiddleware(), urlencodedParser, function(req, res) {
res.send("You are authenticated!");
});
app.listen(8080);
/app/authenticate/init.js:
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const scrypt = require("scrypt");
const auth = require("./userfunctions");
const authenticationMiddleware = function() {
return function (req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.send('ERR: You are not authenticated!');
};
}
module.exports = {
initPassport: function() {
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(id, done) {
auth.getUserById(id, done);
});
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'pwd',
},
function(username, password, done) {
auth.login(username, function(err, user) {
if (err) {console.log(err); return done(err);}
if (!user) {return done(null, false);}
if (!scrypt.verifyKdfSync(user.password, password)) {return done(null, false);}
return done(null, user);
});
}
));
passport.authenticationMiddleware = authenticationMiddleware;
}
};
There are a few other files in /app/authenticate, which are index.js (just adds all the other things to modules.exports) and userfunctions.js, which just includes login and signup functions. I can post these if you need.
I have also omitted the config file, which is just database credentials for PostgreSQL and redis. Finally I have excluded test.html which is served when you GET /, and is just a test HTML login and signup form.
You can use express-session npm module to save your session value.
Below are the steps-
npm install express-session
In your startup JS file write below code
var expressSession = require('express-session');
app.use(expressSession({secret: 'yourothersecretcode', saveUninitialized: true, resave: true}));
Now you can use req.session in your code
req.session is just a json object that gets persisted by the express-session middleware, using a store of your choice e.g. Mongo or Redis.
I'm new to MEAN stack and I'm trying to setup a simple social login using google passport strategy. I can successfully authenticate myself, but when calling a redirect on success, the middle ware function isLoggedIn keeps showing req.isAuthenticated() to be false. Below are code snippets. I saw that there were multiple posts on this issue but none of the answers seem to work for me. Kindly help me resolve this issue.
passport.js
var Auth = require('./auth.js');
var ubCust = require('../models/ubCust.js');
var FacebookStrategy = require('passport-facebook').Strategy;
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
//var FacebookStrategy = require('passport-facebook').Strategy;
module.exports= function(passport){
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
ubCust.findById(id,function(err,user){
done(err, user);
})
});
passport.use(new GoogleStrategy ({
clientID: Auth.googleAuth.clientID,
clientSecret: Auth.googleAuth.clientSecret,
callbackURL : Auth.googleAuth.callbackURL
},
function(token, refreshToken, profile, done) {
// Create or update user, call done() when complete...
process.nextTick(function(){
ubCust.findOne({'google.id' : profile.id}, function(err, user) {
if (err)
return done(err);
if(user)
return done(null,user);
else
{
var newUser = new ubCust;
newUser.google.id = profile.id;
newUser.google.token = token;
newUser.google.name = profile.displayName;
newUser.google.email = profile.emails[0].value;
newUser.save(function(err){
if (err)
throw err;
return done(null,newUser);
});
console.log(profile);
}
//done(null, profile, tokens);
}); //findOne
});//nextTick
}
));
};
route(authController.js)
module.exports = function(app,passport){
app.get('/api/getprofauth',isLoggedIn,function(req,res){
console.log('In getprofile authentication api');
//console.log('req.query :',req.query);
res.send(req.user);
});
app.get('/auth/google',
passport.authenticate('google',{scope: ['email','profile']})
);
app.get('/auth/google/callback',
passport.authenticate('google', {failureRedirect: '/'}),
function(req, res) {
console.log('auth success!! ',req.isAuthenticated(),req.user);
res.redirect('/api/getprofauth');
}
);
function isLoggedIn(req,res,next) {
// if user is authenticated in the session, carry on
if (req.isAuthenticated())
{
console.log('isAuthenticated Success');
return next();
}
else{
console.log('req.user',req.user,req.isAuthenticated());
console.log('isAuthenticated Failure');
res.redirect('/');
}
// if they aren't redirect them to the home page
}//isLoggedIn
}
app.js(server)
var express = require('express');
var app = express();
var mongoose = require('mongoose');
var config = require('./config');
var passport = require('passport');
var cookieParser = require('cookie-parser');
app.use(cookieParser());
var session = require('express-session');
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}));
var authController = require('./controllers/authController.js');
var port = process.env.PORT||3000;
app.use(express.static(__dirname+ '/public'));
app.set('view engine','ejs');
app.get('/',function(req,res){
//redirecting request to home page '/' to index page
res.redirect('/index.htm');
});
mongoose.connect(config.getDBConnectionString());
app.use(passport.initialize());
app.use(passport.session());
var pp = require('./config/passport');
pp(passport);
apiController(app);
apibike(app);
appoController(app);
authController(app,passport);
app.listen(port);
I'm trying to use passport local strategy in my app but the passport variable is undefined on the index.js route. Below is my codes from app.js, passport.js and my index.js route.
Here is my app.js:
var express = require("express");
var path = require('path');
var load = require("express-load");
var bodyParser = require("body-parser");
var cookieParser = require("cookie-parser");
var expressSession = require("express-session");
var methodOverride = require('method-override');
var passport = require('passport');
var error = require('./middlewares/error');
var app = express();
var server = require('http').Server(app);
global.mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test');
require('./config/passport')(passport);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(cookieParser('talkieparty'));
app.use(expressSession({
resave: false,
saveUninitialized: false,
secret: "149194"
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(methodOverride('_method'));
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(path.join(__dirname, 'public')));
load('models').then('controllers').then('routes').into(app, passport);
app.use(error.notFound);
app.use(error.serverError);
var User = app.models.user;
var moment = require('moment');
process.on('message', function(data) {//Executa um função ordenada pelo master.
console.log(process.pid + " executar: " + JSON.stringify(data));
var action = data.action;
switch(action) {
case "confirmation-email-sended":
var email = data.data.email;
User.findOne({email: email}, function(error, user) {
if(error) {
console.log(error);
}else {
if(!user) {
return console.log("No user was found with e-mail like " + email);
}
user.register.notified = true;
user.register.notified_date = new Date(moment().format());
user.save(function(error, user) {
if(error) {
console.log(error);
}else {
console.log("User " + user.email + " received the confirmation e-mail successfully!");
}
});
}
});
break;
default:
console.log("No action was found for " + action);
break;
};
});
server.listen(80, function() {
console.log("Listening!");
});
Here is my passport.js:
var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/user');
module.exports = function(passport) {
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('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) return done(null, false, null);
if(!user.comparePasswordHashes(data.pass, user.register.password)) return done(null, false, null);
return done(null);
})
}));
}
And here is my index.js route where i'm getting the passport undefined:
module.exports = function(app, passport) {
var indexController = app.controllers.index;
app.get('/', indexController.index);
app.post('/login', indexController.login);
app.post('/register', indexController.register);
app.post('/passport', passport.authenticate('local-login', {
successRedirect: '/logado',
failureRedirect: '/falhou',
failureFlash: false
}));
};