In a MeteorJS app, I need to prevent some users from logging in.
Particularly, if the users has been using a very old password, exceeding 90 days for example, we want to force them to reset password before they can login again.
As a MeteorJS beginner trying to achieve this, I have the following codes in a admin login javascript file, somewhere in the views folder:
Template.adminLogin.events({
'submit #frm-admin-login': function(e) {
e.preventDefault();
if ($('#frm-admin-login').validate().form()) {
var loginProperties = {
email: $(e.target).find('input[name=email]').val(),
password: $(e.target).find('input[name=password]').val()
};
Meteor.loginWithPassword(loginProperties.email, loginProperties.password, function(err) {
if (err) return throwError(err);
if (Meteor.userId()) {
let seconds = new Date().getTime() - Meteor.user().lastPasswordResetAt.getTime();
let days = seconds / (1000*60*60*24);
let daysToForceResetPassword = parseInt(Meteor.DAYS_TO_FORCE_RESET_PASSWORD);
$(e.target).find('input[name=password]').val('');
if (days > daysToForceResetPassword){
// log out user, and
// show error that you must reset password in order to login
}else{
// no problem, proceed to dashboard
MNotifications.clear({type: 'error'});
Router.go('adminDashboard');
}
}
});
}
}
});
This works, but it feels strange that I am first allowing the users to login, and then we immediately log him out from the view layer once we realise that he needs to reset password immediately.
Is this done correctly, or is there a better way to customise or modify the Meteor.loginWithPassword() method internally, so that it does not even allow login at all?
You should do this check on the server using validateLoginAttempt
docs
By throwing a Meteor.Error
, you can report a particular error message to the client and alert the user to reset their password from there:
Example implementation:
// on server
Accounts.validateLoginAttempt(function (attempt) {
if (!attempt.allowed) return false;
if (attempt.user) {
let seconds = attempt.user.lastPasswordResetAt.getTime();
let days = seconds / (1000 * 60 * 60 * 24);
let daysToForceResetPassword = parseInt(Meteor.settings.DAYS_TO_FORCE_RESET_PASSWORD);
if (days > daysToForceResetPassword) {
throw new Meteor.Error('expired-password')
}
}
return true;
});
// on client
Meteor.loginWithPassword(loginProperties.email, loginProperties.password, function(err) {
if (err.error === 'expired-password') {
// show error that you must reset password in order to login
}
if (err) return throwError(err);
// else no problem, proceed to dashboard
MNotifications.clear({type: 'error'});
Router.go('adminDashboard');
});