Skip to content

Instantly share code, notes, and snippets.

@wootwoot1234
Created August 11, 2017 17:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wootwoot1234/d6ce91b7a83f5dcbcbe742bd449a94bf to your computer and use it in GitHub Desktop.
Save wootwoot1234/d6ce91b7a83f5dcbcbe742bd449a94bf to your computer and use it in GitHub Desktop.
passport-local-mongoose upgrade strategy toubleshoot
// dependencies
var path = require('path');
var express = require('express');
var http = require('http');
var mongoose = require('mongoose');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var bodyParser = require('body-parser');
var MongoStore = require('connect-mongo')(express);
var timeout = express.timeout;
// main config
var app = express();
app.set('port', process.env.PORT || 1337);
app.set('views', __dirname + '/views');
//app.set('view engine', 'jade');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
//app.set('view options', { layout: false });
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
app.use(express.logger());
//app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
//app.use(express.session());
app.use(express.session({
secret: '1234123412341234',
maxAge: new Date(Date.now() + 3600000),
store: new MongoStore({mongooseConnection:mongoose.connection})
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
app.configure('development', function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use(express.errorHandler());
});
// passport config
var User = require('./models/user');
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
// mongoose
var uristring = process.env.MONGODB_URI || 'mongodb://localhost/passport_local_mongoose';
mongoose.connect(uristring);
// routes
require('./routes')(app);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (process.env.NODE_ENV != "production") {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
app.listen(app.get('port'), function(){
console.log(("Express server listening on port " + app.get('port')));
if(process.env.NODE_ENV != "production") {
console.log("NOT RUNNING IN PRODUCTION MODE, APP WILL NOT SEND MESSAGES OR SET PRICES");
}
});
const passportLocalMongoose = require('passport-local-mongoose')
const mongoose = require('mongoose')
/**
* Wrap passport-local-mongoose plugin to allow the update of
* the passwords digestAlgorithm, from old `sha1` to a more secure `sha512`
*/
module.exports = function authMongoose (schema, options) {
const digestAlgorithm = 'sha512'
schema.plugin(passportLocalMongoose, {
usernameField: 'email',
errorMessages: {
AttemptTooSoonError: 'signin.errors.attempt-too-soon',
TooManyAttemptsError: 'signin.errors.too-many-attempts',
IncorrectPasswordError: 'signin.errors.incorrect-password',
IncorrectUsernameError: 'signin.errors.incorrect-username'
},
selectFields: '+digestAlgorithm',
digestAlgorithm: digestAlgorithm,
limitAttempts: true,
maxAttempts: 50
})
schema.add({
digestAlgorithm: { type: String, select: false }
})
/**
* Override .setPassword to also save `digestAlgorithm` key
*/
const originalSetPassword = schema.methods.setPassword
schema.methods.setPassword = function setPassword (pass, cb) {
return originalSetPassword.call(this, pass, (err) => {
if (err) return cb(err)
this.set('digestAlgorithm', digestAlgorithm)
cb(null, this)
})
}
/**
* Override .authenticate to migrate the password when needed
*/
const originalAuthenticate = schema.methods.authenticate
schema.methods.authenticate = function verifyUserDigestAlgorithm (pass, cb) {
console.log("authenticate");
const user = this
const userDigestAlgorithm = user.get('digestAlgorithm')
if (userDigestAlgorithm === digestAlgorithm) {
return originalAuthenticate.call(user, pass, cb)
}
// Migrate old sha1 password to a new one with sha256
if (!userDigestAlgorithm) {
const sha1User = new Sha1User({
hash: user.get('hash'),
salt: user.get('salt')
})
return sha1User.authenticate(pass, (err, passed, msg) => {
if (err) return cb(err)
if (passed === false) return cb(null, passed, msg)
return user.setPassword(pass, (err, user) => {
if (err) return cb(err)
user.save((err, user) => {
if (err) return cb(err)
originalAuthenticate.call(user, pass, cb)
})
})
})
}
return cb(null, false, {
message: 'signin.errors.must-reset-password'
})
}
}
/**
* Use a temporary dummy model for old passwords verification
*/
const Sha1UserSchema = new mongoose.Schema()
Sha1UserSchema.plugin(passportLocalMongoose, {
errorMessages: {
AttemptTooSoonError: 'signin.errors.attempt-too-soon',
TooManyAttemptsError: 'signin.errors.too-many-attempts',
IncorrectPasswordError: 'signin.errors.incorrect-password',
IncorrectUsernameError: 'signin.errors.incorrect-username'
},
digestAlgorithm: 'sha1',
limitAttempts: false
})
const Sha1User = mongoose.model('Sha1User', Sha1UserSchema)
var passport = require('passport');
var User = require('./models/user');
var Account = require('./models/user.account');
var Listing = require('./models/user.account.listing');
var Message = require('./models/user.account.listing.message');
var MessageRule = require('./models/user.account.listing.messageRule');
var Price = require('./models/user.account.listing.price');
var PriceRule = require('./models/user.account.listing.priceRule');
var Reservation = require('./models/user.account.reservation');
var querystring = require('querystring');
var https = require('https');
var moment = require('moment-timezone');
var mailer = require('./email/email');
var nodemailer = require('nodemailer');
// Generic error handler used by all endpoints.
function handleError(res, reason, message, code) {
console.log("Error: ", reason);
console.log(message);
return res.status(code || 500).json({"error": message});
}
module.exports = function(app) {
app.get('/', function(req, res) {
if(!req.user) {
res.render('index');
} else {
res.redirect('/admin/#/');
}
});
app.get('/admin', function(req, res) {
if(!req.user) {
res.redirect('/#!/login');
} else {
res.render('admin', { user : req.user });
}
});
app.post('/register', function(req, res) {
console.log("/register");
User.register(new User({ username : req.body.username }), req.body.password, function(error, doc) {
if (error) {
return handleError(res, "That email already exists. Try logging in instead.", {error_code: 400, error_message: "That email already exists. Try logging in instead."}, 400);
} else {
passport.authenticate('local')(req, res, function () {
var to = req.user.username;
var subject = "Welcome to Superhost Tools";
var text = "Thanks for joining Superhost Tools. If you haven't already, the next step is to login and link your Airbnb account and begin automatically sending messages to your guests."
sendEmail(to, subject, text);
return res.redirect('/admin/#/');
});
}
});
});
app.post('/login', function(req, res, next) {
console.log("/login");
/* look at the 2nd parameter to the below call */
passport.authenticate('local', function(error, user, info) {
console.log("error", error);
console.log("user", user);
console.log("info", info);
if (error) {
return next(error);
}
if (!user) {
return handleError(res, "That email and password combination is invalid. Try something else.", {error_code: 400, error_message: "That email and password combination is invalid. Try something else."}, 400);
}
req.logIn(user, function(error) {
if (error) {
return next(error);
}
return res.redirect('/admin/#/');
});
})(req, res, next);
});
app.get('/logout', function(req, res) {
console.log("/logout");
req.logout();
res.redirect('/');
});
//...
};
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
passportLocalMongoose = require('passport-local-mongoose'),
//authMongoose = require('./mongoose'),
bcrypt = require('bcrypt-nodejs');
var User = new Schema({
username: String,
password: String,
},
{timestamps: true});
//User.plugin(authMongoose);
// // OLD
// npm install passport@0.1.17 --save
// npm install passport-local@0.1.6 --save
// npm install passport-local-mongoose@0.2.5 --save
// // NEW
// npm install passport@0.3.2 --save
// npm install passport-local@1.0.0 --save
// npm install passport-local-mongoose@4.1.0 --save
User.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', User);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment