mirror of
https://github.com/taobataoma/meanTorrent.git
synced 2026-03-06 12:11:02 +01:00
Implemented password reset core feature
This commit is contained in:
@@ -7,6 +7,12 @@ var mongoose = require('mongoose'),
|
||||
passport = require('passport'),
|
||||
User = mongoose.model('User'),
|
||||
_ = require('lodash');
|
||||
/* Requires for reset password */
|
||||
var nodemailer = require('nodemailer');
|
||||
var LocalStrategy = require('passport-local').Strategy;
|
||||
var bcrypt = require('bcrypt-nodejs');
|
||||
var async = require('async');
|
||||
var crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* Get the error message from error object
|
||||
@@ -92,6 +98,160 @@ exports.signin = function(req, res, next) {
|
||||
})(req, res, next);
|
||||
};
|
||||
|
||||
/**
|
||||
* Forgot for reset password (forgot POST)
|
||||
*/
|
||||
exports.forgot = function(req, res, next) {
|
||||
async.waterfall([
|
||||
// Generate random token
|
||||
function(done) {
|
||||
crypto.randomBytes(20, function(err, buf) {
|
||||
var token = buf.toString('hex');
|
||||
done(err, token);
|
||||
});
|
||||
},
|
||||
// Lookup user by email address
|
||||
function(token, done) {
|
||||
if (req.body.email) {
|
||||
User.findOne({ email: req.body.email }, function(err, user) {
|
||||
if (!user) {
|
||||
return res.send(400, {
|
||||
message: 'No account with that email address exists'
|
||||
});
|
||||
}
|
||||
|
||||
user.resetPasswordToken = token;
|
||||
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
|
||||
|
||||
user.save(function(err) {
|
||||
done(err, token, user);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return res.send(400, {
|
||||
message: 'Email field must not be blank'
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
// If valid email, send reset email using service
|
||||
function(token, user, done) {
|
||||
var smtpTransport = nodemailer.createTransport('SMTP', {
|
||||
service: 'SendGrid', // Choose email service, default SendGrid
|
||||
auth: {
|
||||
user: 'your_sendgrid_email@domain.com',
|
||||
pass: 'your_sendgrid_password'
|
||||
}
|
||||
});
|
||||
var mailOptions = {
|
||||
to: user.email,
|
||||
from: 'your_email@domain.com',
|
||||
subject: 'Password Reset',
|
||||
text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
|
||||
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
|
||||
'http://' + req.headers.host + '/auth/reset/' + token + '\n\n' +
|
||||
'If you did not request this, please ignore this email and your password will remain unchanged.\n'
|
||||
};
|
||||
smtpTransport.sendMail(mailOptions, function(err) {
|
||||
res.send(200, {
|
||||
message: 'An email has been sent to ' + user.email + ' with further instructions.'
|
||||
});
|
||||
done(err, 'done');
|
||||
});
|
||||
}
|
||||
], function(err) {
|
||||
if (err) return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset password GET from email token
|
||||
*/
|
||||
exports.resetGet = function(req, res) {
|
||||
User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
|
||||
if (!user) {
|
||||
// res.render('404');
|
||||
res.send(400, {
|
||||
message: 'Password reset token is invalid or has expired.'
|
||||
});
|
||||
return res.redirect('/#!/forgot');
|
||||
}
|
||||
|
||||
res.redirect('/#!/reset/' + req.params.token);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset password POST from email token
|
||||
*/
|
||||
exports.resetPost = function(req, res) {
|
||||
// Init Variables
|
||||
var passwordDetails = req.body;
|
||||
var message = null;
|
||||
|
||||
async.waterfall([
|
||||
function(done) {
|
||||
User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
|
||||
if (!err && user) {
|
||||
if (passwordDetails.newPassword === passwordDetails.verifyPassword) {
|
||||
user.password = passwordDetails.newPassword;
|
||||
user.resetPasswordToken = undefined;
|
||||
user.resetPasswordExpires = undefined;
|
||||
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
done(err, user);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return res.send(400, {
|
||||
message: 'Passwords do not match'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res.send(400, {
|
||||
message: 'Password reset token is invalid or has expired.'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
function(user, done) {
|
||||
var smtpTransport = nodemailer.createTransport('SMTP', {
|
||||
service: 'SendGrid',
|
||||
auth: {
|
||||
user: 'your_sendgrid_email@domain.com',
|
||||
pass: 'your_sendgrid_password'
|
||||
}
|
||||
});
|
||||
var mailOptions = {
|
||||
to: user.email,
|
||||
from: 'your_email@domain.com',
|
||||
subject: 'Your password has been changed',
|
||||
text: 'Hello,\n\n' +
|
||||
'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
|
||||
};
|
||||
smtpTransport.sendMail(mailOptions, function(err) {
|
||||
res.send(200, {
|
||||
message: 'Password changed successfully'
|
||||
});
|
||||
});
|
||||
}
|
||||
], function(err) {
|
||||
res.redirect('/');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update user details
|
||||
*/
|
||||
|
||||
@@ -81,7 +81,14 @@ var UserSchema = new Schema({
|
||||
created: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
}
|
||||
},
|
||||
/* For reset password */
|
||||
resetPasswordToken: {
|
||||
type: String
|
||||
},
|
||||
resetPasswordExpires: {
|
||||
type: Date
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,9 @@ module.exports = function(app) {
|
||||
app.route('/auth/signup').post(users.signup);
|
||||
app.route('/auth/signin').post(users.signin);
|
||||
app.route('/auth/signout').get(users.signout);
|
||||
app.route('/auth/forgot').post(users.forgot);
|
||||
app.route('/auth/reset/:token').get(users.resetGet);
|
||||
app.route('/auth/reset/:token').post(users.resetPost);
|
||||
|
||||
// Setting the facebook oauth routes
|
||||
app.route('/auth/facebook').get(passport.authenticate('facebook', {
|
||||
|
||||
@@ -41,7 +41,10 @@
|
||||
"forever": "~0.11.0",
|
||||
"bower": "~1.3.1",
|
||||
"grunt-cli": "~0.1.13",
|
||||
"glob": "~3.2.9"
|
||||
"glob": "~3.2.9",
|
||||
"bcrypt-nodejs": "0.0.3",
|
||||
"async": "~0.8.0",
|
||||
"nodemailer": "~0.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"supertest": "~0.10.0",
|
||||
|
||||
2
public/dist/application.min.js
vendored
2
public/dist/application.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -24,6 +24,14 @@ angular.module('users').config(['$stateProvider',
|
||||
state('signin', {
|
||||
url: '/signin',
|
||||
templateUrl: 'modules/users/views/signin.client.view.html'
|
||||
}).
|
||||
state('forgot', {
|
||||
url: '/forgot',
|
||||
templateUrl: 'modules/users/views/forgot.client.view.html'
|
||||
}).
|
||||
state('reset', {
|
||||
url: '/reset/:token',
|
||||
templateUrl: 'modules/users/views/reset.client.view.html'
|
||||
});
|
||||
}
|
||||
]);
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('users').controller('AuthenticationController', ['$scope', '$http', '$location', 'Authentication',
|
||||
function($scope, $http, $location, Authentication) {
|
||||
angular.module('users').controller('AuthenticationController', ['$scope', '$stateParams', '$http', '$location', 'Authentication',
|
||||
function($scope, $stateParams, $http, $location, Authentication) {
|
||||
$scope.authentication = Authentication;
|
||||
|
||||
//If user is signed in then redirect back home
|
||||
@@ -30,5 +30,37 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$http
|
||||
$scope.error = response.message;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.forgot = function() {
|
||||
$scope.success = $scope.error = null;
|
||||
|
||||
$http.post('/auth/forgot', $scope.credentials).success(function(response) {
|
||||
// Show user success message and clear form
|
||||
$scope.credentials = null;
|
||||
$scope.success = response.message;
|
||||
|
||||
}).error(function(response) {
|
||||
// Show user error message and clear form
|
||||
$scope.credentials = null;
|
||||
$scope.error = response.message;
|
||||
});
|
||||
};
|
||||
|
||||
// Change user password
|
||||
$scope.reset = function() {
|
||||
$scope.success = $scope.error = null;
|
||||
|
||||
$http.post('/auth/reset/' + $stateParams.token,
|
||||
$scope.passwordDetails).success(function(response) {
|
||||
|
||||
// If successful show success message and clear form
|
||||
$scope.success = response.message;
|
||||
$scope.passwordDetails = null;
|
||||
|
||||
}).error(function(response) {
|
||||
$scope.error = response.message;
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
]);
|
||||
21
public/modules/users/views/forgot.client.view.html
Normal file
21
public/modules/users/views/forgot.client.view.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<section class="row" data-ng-controller="AuthenticationController">
|
||||
<h3 class="col-md-12 text-center">Forgot your password?</h3>
|
||||
<div class="col-xs-offset-2 col-xs-8 col-md-offset-5 col-md-2">
|
||||
<form data-ng-submit="forgot()" class="signin form-horizontal" autocomplete="off">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<input type="text" id="email" name="email" class="form-control" data-ng-model="credentials.email" placeholder="Account Email">
|
||||
</div>
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
<div data-ng-show="error" class="text-center text-danger">
|
||||
<strong>{{error}}</strong>
|
||||
</div>
|
||||
<div data-ng-show="success" class="text-center text-success">
|
||||
<strong>{{success}}</strong>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
26
public/modules/users/views/reset.client.view.html
Normal file
26
public/modules/users/views/reset.client.view.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<section class="row" data-ng-controller="AuthenticationController">
|
||||
<h3 class="col-md-12 text-center">Reset your password</h3>
|
||||
<div class="col-xs-offset-2 col-xs-8 col-md-offset-5 col-md-2">
|
||||
<form data-ng-submit="reset()" class="signin form-horizontal" autocomplete="off">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label for="newPassword">New Password</label>
|
||||
<input type="password" id="newPassword" name="newPassword" class="form-control" data-ng-model="passwordDetails.newPassword" placeholder="New Password">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="verifyPassword">Verify Password</label>
|
||||
<input type="password" id="verifyPassword" name="verifyPassword" class="form-control" data-ng-model="passwordDetails.verifyPassword" placeholder="Verify Password">
|
||||
</div>
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-large btn-primary">Update Password</button>
|
||||
</div>
|
||||
<div data-ng-show="error" class="text-center text-danger">
|
||||
<strong>{{error}}</strong>
|
||||
</div>
|
||||
<div data-ng-show="success" class="text-center text-success">
|
||||
<strong>{{success}}</strong>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
@@ -30,6 +30,9 @@
|
||||
<button type="submit" class="btn btn-primary">Sign in</button> or
|
||||
<a href="/#!/signup">Sign up</a>
|
||||
</div>
|
||||
<div class"forgot-password">
|
||||
<a href="/#!/forgot">Forgot your password?</a>
|
||||
</div>
|
||||
<div data-ng-show="error" class="text-center text-danger">
|
||||
<strong>{{error}}</strong>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user