[#1446] add password-reset (still need change password for this to be useful)

This commit is contained in:
Tyler Renelle 2013-09-06 09:33:23 -04:00
parent 883d63aaaf
commit c12de954a4
6 changed files with 97 additions and 7 deletions

View file

@ -82,5 +82,15 @@ habitrpg.controller("AuthCtrl", ['$scope', '$rootScope', 'User', '$http', '$loca
$('#login-modal').modal('show');
}
}
$scope.passwordReset = function(email){
$http.post(API_URL + '/api/v1/user/reset-password', {email:email})
.success(function(){
alert('New password sent.');
})
.error(function(data){
alert(data.err);
});
}
}
]);

View file

@ -30,7 +30,8 @@
"nib": "~1.0.1",
"jade": "~0.35.0",
"passport": "~0.1.17",
"validator": "~1.5.1"
"validator": "~1.5.1",
"nodemailer": "~0.5.2"
},
"private": true,
"subdomain": "habitrpg",

View file

@ -5,7 +5,8 @@ var sanitize = validator.sanitize;
var passport = require('passport');
var helpers = require('habitrpg-shared/script/helpers');
var async = require('async');
var derbyAuthUtil = require('derby-auth/utils');
var utils = require('../utils');
var nconf = require('nconf');
var User = require('../models/user').model;
var api = module.exports;
@ -73,7 +74,7 @@ api.registerUser = function(req, res, next) {
return cb("Username already taken");
}
newUser = helpers.newUser(true);
salt = derbyAuthUtil.makeSalt();
salt = utils.makeSalt();
newUser.auth = {
local: {
username: username,
@ -82,7 +83,7 @@ api.registerUser = function(req, res, next) {
},
timestamps: {created: +new Date(), loggedIn: +new Date()}
};
newUser.auth.local.hashed_password = derbyAuthUtil.encryptPassword(password, salt);
newUser.auth.local.hashed_password = utils.encryptPassword(password, salt);
user = new User(newUser);
user.save(cb);
}
@ -111,7 +112,7 @@ api.loginLocal = function(req, res, next) {
// We needed the whole user object first so we can get his salt to encrypt password comparison
User.findOne({
'auth.local.username': username,
'auth.local.hashed_password': derbyAuthUtil.encryptPassword(password, user.auth.local.salt)
'auth.local.hashed_password': utils.encryptPassword(password, user.auth.local.salt)
}, cb);
}
], function(err, user) {
@ -160,6 +161,29 @@ api.loginFacebook = function(req, res, next) {
});
};
api.resetPassword = function(req, res, next){
var email = req.body.email,
salt = utils.makeSalt(),
newPassword = utils.makeSalt(), // use a salt as the new password too (they'll change it later)
hashed_password = utils.encryptPassword(newPassword, salt);
User.findOne({'auth.local.email':email}, function(err, user){
if (err) return res.json(500,{err:err});
if (!user) return res.send(500, {err:"Couldn't find a user registered for email " + email});
user.auth.local.salt = salt;
user.auth.local.hashed_password = hashed_password;
utils.sendEmail({
from: "HabitRPG <admin@habitrpg.com>",
to: email,
subject: "Password Reset for HabitRPG",
text: "Password for " + user.auth.local.username + " has been reset to " + newPassword + ". Log in at " + nconf.get('BASE_URL'),
html: "Password for <strong>" + user.auth.local.username + "</strong> has been reset to <strong>" + newPassword + "</strong>. Log in at" + nconf.get('BASE_URL')
});
user.save();
return res.send('New password sent to '+ email);
});
};
/*
Registers a new user. Only accepting username/password registrations, no Facebook
*/

View file

@ -7,5 +7,6 @@ auth.setupPassport(router); //FIXME make this consistent with the others
router.post('/api/v1/register', auth.registerUser);
router.post('/api/v1/user/auth/local', auth.loginLocal);
router.post('/api/v1/user/auth/facebook', auth.loginFacebook);
router.post('/api/v1/user/reset-password', auth.resetPassword);
module.exports = router;

34
src/utils.js Normal file
View file

@ -0,0 +1,34 @@
var nodemailer = require('nodemailer');
var nconf = require('nconf');
var crypto = require('crypto');
module.exports.sendEmail = function(mailData) {
var smtpTransport = nodemailer.createTransport("SMTP",{
service: nconf.get('SMTP_SERVICE'),
auth: {
user: nconf.get('SMTP_USER'),
pass: nconf.get('SMTP_PASS')
}
});
smtpTransport.sendMail(mailData, function(error, response){
if(error){
console.log(error);
}else{
console.log("Message sent: " + response.message);
}
smtpTransport.close(); // shut down the connection pool, no more messages
});
}
// Encryption using http://dailyjs.com/2010/12/06/node-tutorial-5/
// Note: would use [password-hash](https://github.com/davidwood/node-password-hash), but we need to run
// model.query().equals(), so it's a PITA to work in their verify() function
module.exports.encryptPassword = function(password, salt) {
return crypto.createHmac('sha1', salt).update(password).digest('hex');
}
module.exports.makeSalt = function() {
var len = 10;
return crypto.randomBytes(Math.ceil(len / 2)).toString('hex').substring(0, len);
}

View file

@ -41,14 +41,34 @@ block content
input(type='text', ng-model='loginUsername', placeholder='{{useUUID ? "UUID" : "Username"}}')
.control-group
input(type='{{useUUID ? "text" : "password"}}', ng-model='loginPassword', placeholder='{{useUUID ? "API Token" : "Password"}}')
.control-group
//-.control-group
label.checkbox
input(type='checkbox', ng-click='useUUID = !useUUID')
| Use UUID / API Token (For Facebook Users)
.control-group
input.btn.btn-primary(type='submit', value='Login')
.tab-pane#register-tab
// good god accordions have html ceremony
#forgot-password-accordion.accordion
.accordion-group
.accordion-heading
a.accordion-toggle(data-toggle='collapse', data-parent='#forgot-password-accordion', href='#forgot-password-group')
| Forgot Password
#forgot-password-group.accordion-body.collapse
.accordion-inner
form#derby-auth-password-reset(ng-submit='passwordReset(passwordResetEmail)')
h3 Email New Password
//.alert.alert-success {.success.passwordReset}
//.control-group.{#if..errors.passwordReset}error{/}
.control-group
input(type='text', name='email', placeholder='Email', ng-model='passwordResetEmail')
//span.help-inline {.errors.passwordReset}
input.btn(type='submit', value='Submit')
.tab-pane#register-tab
form(ng-submit='register()', name='registrationForm')
.control-group
input(type='text', ng-model='registerVals.username', placeholder='Username', required)