I use Passport.js in Node.js to create a login system. Everything is ok, but I do not know how to reset user password when they forget their password or they want to change it.
User model in MongoDB
var UserSchema = new Schema({ email: String, username: String, provider: String, hashed_password: String, salt: String, });
You can reset or change passwords using 2 simple functions in passport-local-mongoose. They are setPassword function and changePassword functions. Normally setPassword is used when the user forgot the password and changePassword is used when the user wants to change the password.
The local authentication strategy authenticates users using a username and password. The strategy requires a verify callback, which accepts these credentials and calls done providing a user.
Passport is authentication middleware for Node. js. As it's extremely flexible and modular, Passport can be unobtrusively dropped into any Express-based web application. A comprehensive set of strategies supports authentication using a username and password, Facebook, Twitter, and more.
Didn't really like the idea of hitting my database to store tokens, especially when you want to be creating and verifying tokens for many actions.
Instead I decided to copy how Django does it:
today
ident
hash
containing: :ident
/:today
-:hash
We test the req.params.timestamp in order to simply test if it's valid for today, cheapest test first. fail first.
Then we find the user, fail if it doesn't exist.
Then we generate the hash again from above, but with the timestamp from req.params
The reset link becomes invalid if :
This way:
I tried to use node-password-reset as Matt617 suggested but didn't really care for it. It's about the only thing that's coming up in searches currently.
So some hours digging around, I found it easier to implement this on my own. In the end it took me about a day to get all the routes, UI, emails and everything working. I still need to enhance security a bit (reset counters to prevent abuse, etc.) but got the basics working:
Here's my code for generating a token (taken from node-password-reset):
function generateToken() { var buf = new Buffer(16); for (var i = 0; i < buf.length; i++) { buf[i] = Math.floor(Math.random() * 256); } var id = buf.toString('base64'); return id; }
Hope this helps.
EDIT: Here's the app.js. Note I'm keeping the entire user object in the session. I plan on moving to couchbase or similar in the future.
var express = require('express'); var path = require('path'); var favicon = require('static-favicon'); var flash = require('connect-flash'); var morgan = require('morgan'); var cookieParser = require('cookie-parser'); var cookieSession = require('cookie-session'); var bodyParser = require('body-parser'); var http = require('http'); var https = require('https'); var fs = require('fs'); var path = require('path'); var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy; var app = express(); app.set('port', 3000); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jade'); var cookies = cookieSession({ name: 'abc123', secret: 'mysecret', maxage: 10 * 60 * 1000 }); app.use(cookies); app.use(favicon()); app.use(flash()); app.use(morgan()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded()); app.use(cookieParser()); app.use(passport.initialize()); app.use(passport.session()); app.use(express.static(path.join(__dirname, 'public'))); module.exports = app; passport.use(new LocalStrategy(function (username, password, done) { return users.validateUser(username, password, done); })); //KEEP ENTIRE USER OBJECT IN THE SESSION passport.serializeUser(function (user, done) { done(null, user); }); passport.deserializeUser(function (user, done) { done(null, user); }); //Error handling after everything else app.use(logErrors); //log all errors app.use(clientErrorHandler); //special handler for xhr app.use(errorHandler); //basic handler http.createServer(app).listen(app.get('port'), function () { console.log('Express server listening on HTTP port ' + app.get('port')); });
EDIT: Here are the routes.
app.get('/forgot', function (req, res) { if (req.isAuthenticated()) { //user is alreay logged in return res.redirect('/'); } //UI with one input for email res.render('forgot'); }); app.post('/forgot', function (req, res) { if (req.isAuthenticated()) { //user is alreay logged in return res.redirect('/'); } users.forgot(req, res, function (err) { if (err) { req.flash('error', err); } else { req.flash('success', 'Please check your email for further instructions.'); } res.redirect('/'); }); }); app.get('/reset/:token', function (req, res) { if (req.isAuthenticated()) { //user is alreay logged in return res.redirect('/'); } var token = req.params.token; users.checkReset(token, req, res, function (err, data) { if (err) req.flash('error', err); //show the UI with new password entry res.render('reset'); }); }); app.post('/reset', function (req, res) { if (req.isAuthenticated()) { //user is alreay logged in return res.redirect('/'); } users.reset(req, res, function (err) { if (err) { req.flash('error', err); return res.redirect('/reset'); } else { req.flash('success', 'Password successfully reset. Please login using new password.'); return res.redirect('/login'); } }); });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With