periodicjs.ext.login
Version:
An authentication extension for periodicjs that uses passport to authenticate user sessions.
428 lines (411 loc) • 13.8 kB
JavaScript
;
var passport = require('passport'),
merge = require('utils-merge'),
path = require('path'),
CoreUtilities,
CoreController,
CoreMailer,
appSettings,
mongoose,
User,
logger,
configError,
loginExtSettings,
passportController;
var periodic;
/**
* logins a user using passport's local strategy, if a user is passed to this function, then the user will be logged in and req.user will be populated
* @param {object} req
* @param {object} res
* @return {object} reponds with an error page or sends user to authenicated in resource
*/
var login = function (req, res, next) {
console.log('in start of auth login');
req.controllerData = req.controllerData || {};
if (configError) {
next(configError);
}
else {
passport.authenticate('local', function (err, user, info) {
var resOptions = {
req: req,
res: res,
err: err,
};
if (err) {
CoreController.logError({
req: req,
err: err
});
if (/Your Account is Currently Blocked/gi.test(err.message)) {
req.flash('error', 'Your account is currently blocked');
resOptions.responseData = {
result: 'error',
data: {
message: err.message,
redirecturl: req.session.return_url || loginExtSettings.settings.authLoggedInHomepage
}
};
resOptions.err = err;
resOptions.redirecturl = req.session.return_url || loginExtSettings.settings.authLoggedInHomepage;
return CoreController.respondInKind(resOptions);
}
return next(err);
}
if (!user) {
// logger.verbose('info var',info);
var loginAttemptWarningText = (loginExtSettings.timeout.use_limiter && ((loginExtSettings.timeout.attempts - info.login_attempts) < 4)) ? ` ( You have ${(loginExtSettings.timeout.attempts - info.login_attempts)} more login attempt${(function(attempts_left){
if(attempts_left>1){
return 's';
}
else{
return '';
}
})(loginExtSettings.timeout.attempts - info.login_attempts)} before your account will be locked out )` : '';
var wrongLoginMessage = 'Invalid credentials, did you forget your password?' + loginAttemptWarningText;
CoreController.logWarning({
req: req,
err: new Error(wrongLoginMessage)
});
req.flash('error', wrongLoginMessage);
resOptions.responseData = {
result: 'error',
data: {
message: wrongLoginMessage,
redirecturl: req.session.return_url || loginExtSettings.settings.authLoggedInHomepage
}
};
resOptions.err = resOptions.err || new Error(wrongLoginMessage);
resOptions.redirecturl = req.session.return_url || loginExtSettings.settings.authLoggedInHomepage;
return CoreController.respondInKind(resOptions);
}
else if (req.controllerData.customlogin) {
console.log('in login ext custom login');
resOptions.redirecturl = req.session.return_url || loginExtSettings.settings.authLoggedInHomepage;
resOptions.responseData = {
result: 'success',
data: {
message: 'successfully logged in',
redirecturl: req.session.return_url || loginExtSettings.settings.authLoggedInHomepage
}
};
return CoreController.respondInKind(resOptions);
}
else {
req.logIn(user, function (err) {
if (err) {
CoreController.logWarning({
req: req,
err: err
});
return next(err);
}
else {
resOptions.redirecturl = req.session.return_url || loginExtSettings.settings.authLoggedInHomepage;
resOptions.responseData = {
result: 'success',
data: {
message: 'successfully logged in',
redirecturl: req.session.return_url || loginExtSettings.settings.authLoggedInHomepage
}
};
return CoreController.respondInKind(resOptions);
}
});
}
})(req, res, next);
}
};
/**
* logs user out and destroys user session
* @param {object} req
* @param {object} res
* @return {object} sends user to logout resource
*/
var logout = function (req, res) {
// console.log('req.flash() pre logout', req.flash());
req.logout();
req.session.destroy(function (err) {
// console.log('req.flash() post logout', req.flash());
var resOptions = {
req: req,
res: res,
err: err,
};
if (err) {
logger.error('failed logout', err);
}
req.flash = function () {
return {};
};
resOptions.redirecturl = loginExtSettings.settings.authLoggedOutHomepage || loginExtSettings.settings.authLoggedInHomepage;
resOptions.responseData = {
result: 'success',
data: {
redirecturl: loginExtSettings.settings.authLoggedOutHomepage || loginExtSettings.settings.authLoggedInHomepage
}
};
CoreController.respondInKind(resOptions);
});
};
/**
* keep a user logged in for 30 days
* @param {object} req
* @param {object} res
* @return {Function} next() callback
*/
var rememberme = function (req, res, next) {
// console.log('using remember me');
if (req.method === 'POST' && req.url === loginExtSettings.settings.authLoginPath) {
if (req.body.rememberme) {
req.session.cookie.maxAge = 2592000000; // 30*24*60*60*1000 Rememeber 'me' for 30 days
}
else {
req.session.cookie.expires = false;
}
}
next();
};
var forceAuthLogin = function (req, res) {
if (req.originalUrl) {
req.session.return_url = req.originalUrl;
res.redirect(loginExtSettings.settings.authLoginPath + '?return_url=' + req.originalUrl);
}
else {
res.redirect(loginExtSettings.settings.authLoginPath);
}
};
/**
* make sure a user is authenticated, if not logged in, send them to login page and return them to original resource after login
* @param {object} req
* @param {object} res
* @return {Function} next() callback
*/
var ensureAuthenticated = function (req, res, next) {
if (configError) {
next(configError);
}
else {
/* if a user is logged in, and requires to link account, update the user document with social credentials and then pass to the next express middleware */
if (req.isAuthenticated()) {
if (req.session.linkaccount === true) {
var updateuser = {};
updateuser.attributes = merge(req.user.attributes, req.session.linkaccountdata);
CoreController.updateModel({
cached: req.headers.periodicCache !== 'no-periodic-cache',
model: User,
id: req.user._id,
updatedoc: updateuser,
res: res,
req: req,
callback: function (err /* , updateduser */ ) {
if (err) {
next(err);
}
else {
logger.verbose('linked ', req.session.linkaccountservice, ' account for ', req.user.id, req.user.email, req.user.username);
req.session.linkaccount = false;
delete req.session.linkaccount;
delete req.session.linkaccountdata;
delete req.session.linkaccountservice;
next();
}
}
});
// next(new Error('cannot link '+req.session.linkaccountservice+' account'));
// res.redirect('/user/linkaccount?service='+req.session.linkaccountservice);
}
else if (loginExtSettings && loginExtSettings.settings.disablesocialsignin === true && req.user.accounttype === 'social-sign-in' && req.query.required !== 'social-sign-in' && req.method === 'GET') {
res.redirect('/auth/user/finishregistration?reason=social-sign-in-pending');
}
else if (loginExtSettings && loginExtSettings.settings.requireusername !== false && !req.user.username && req.query.required !== 'username' && req.method === 'GET') {
res.redirect('/auth/user/finishregistration?required=username');
// return next();
}
else if (loginExtSettings && loginExtSettings.settings.requireemail !== false && !req.user.email && req.query.required !== 'email' && req.method === 'GET') {
res.redirect('/auth/user/finishregistration?required=email');
}
else if (loginExtSettings && loginExtSettings.settings.requireemail !== false && !req.user.email && req.query.required !== 'email' && req.method === 'GET') {
res.redirect('/auth/user/finishregistration?required=email');
}
else if (loginExtSettings && loginExtSettings.settings.requireuseractivation && req.user.activated === false && req.query.required !== 'activation' && req.method === 'GET') {
res.redirect('/auth/user/finishregistration?required=activation');
}
else {
return next();
}
}
else {
if (req.query.format === 'json') {
res.send({
'result': 'error',
'data': {
error: 'authentication requires '
}
});
}
else {
logger.verbose('controller - login/user.js - ' + req.originalUrl);
forceAuthLogin(req, res);
}
}
}
};
//GET auth/user/activate
var get_activation = function (req, res, next) {
if (periodic.app.controller.extension.reactadmin) {
let reactadmin = periodic.app.controller.extension.reactadmin;
// console.log({ reactadmin });
// console.log('ensureAuthenticated req.session', req.session);
// console.log('ensureAuthenticated req.user', req.user);
next();
} else {
if (req.isAuthenticated()) {
CoreController.getPluginViewDefaultTemplate({
viewname: 'user/activate',
themefileext: appSettings.templatefileextension,
extname: 'periodicjs.ext.login'
},
function (err, templatepath) {
CoreController.handleDocumentQueryRender({
res: res,
req: req,
renderView: templatepath,
responseData: {
pagedata: {
title: 'Activation Email'
},
user: req.user
}
});
}
);
}
else {
forceAuthLogin(req, res);
}
}
};
//POST to auth/user/activate
var activate_user = function (req, res, next) {
var emailviewname = loginExtSettings.settings.activateEmailTemplate || 'email/user/welcome_with_validation';
if (req.isAuthenticated()) {
CoreController.getPluginViewDefaultTemplate({
viewname: emailviewname,
themefileext: appSettings.templatefileextension
},
function (err, templatepath) {
// console.log('templatepath', templatepath);
if (loginExtSettings.settings.activateEmailTemplate) {
templatepath = path.resolve(process.cwd(), loginExtSettings.settings.activateEmailTemplate);
}
else if (templatepath === emailviewname) {
templatepath = path.resolve(process.cwd(), 'app/views', templatepath + '.' + appSettings.templatefileextension);
}
if (err) {
CoreController.handleDocumentQueryErrorResponse({
err: err,
res: res,
req: req
});
}
else {
var coreMailerOptions = {
appenvironment: appSettings.application.environment,
to: req.user.email,
bcc: (loginExtSettings.settings.useBccOnActivation) ? appSettings.adminnotificationemail : '',
replyTo: appSettings.fromemail || appSettings.adminnotificationemail,
from: appSettings.fromemail || appSettings.adminnotificationemail,
subject: loginExtSettings.settings.activationEmailSubject || appSettings.name + ' User Account Activation',
emailtemplatefilepath: templatepath,
emailtemplatedata: {
user: req.user,
hostname: req.headers.host,
appname: appSettings.name,
filename: templatepath
}
};
if (loginExtSettings.settings.adminbccemail || appSettings.adminbccemail) {
coreMailerOptions.bcc = loginExtSettings.settings.adminbccemail || appSettings.adminbccemail;
}
CoreMailer.sendEmail(coreMailerOptions, function (sendemailerr, emailstatus) {
if (sendemailerr) {
CoreController.handleDocumentQueryErrorResponse({
err: sendemailerr,
res: res,
req: req
});
}
else {
logger.silly('emailstatus', emailstatus);
req.flash('info', 'A new verification email as been sent to ' + req.user.email);
if (req.controllerData && req.controllerData.sendemailstatus) {
req.controllerData.activate_user_emailstatus = emailstatus;
next();
}
else {
res.redirect(loginExtSettings.settings.authLoggedInHomepage);
}
}
});
}
}
);
}
else {
forceAuthLogin(req, res);
}
};
/**
* login controller
* @module authController
* @{@link https://github.com/typesettin/periodic}
* @author Yaw Joseph Etse
* @copyright Copyright (c) 2014 Typesettin. All rights reserved.
* @license MIT
* @requires module:passport
* @requires module:path
* @requires module:passport-local
* @requires module:passport-facebook
* @requires module:fs-extra
* @requires module:periodicjs.core.utilities
* @requires module:periodicjs.core.controller
* @requires module:periodicjs.core.extensions
* @param {object} resources variable injection from current periodic instance with references to the active logger and mongo session
* @return {object} sendmail
*/
var controller = function (resources, UserModel) {
logger = resources.logger;
mongoose = resources.mongoose;
appSettings = resources.settings;
User = UserModel || mongoose.model('User');
CoreController = resources.core.controller;
CoreUtilities = resources.core.utilities;
CoreMailer = resources.core.extension.mailer;
periodic = resources;
// var appenvironment = appSettings.application.environment;
loginExtSettings = resources.app.controller.extension.login.loginExtSettings;
passportController = require('./passport_controller')(resources, {
User: User,
loginExtSettings: loginExtSettings,
passport: passport
});
passportController.serialize();
passportController.deserialize();
passport = passportController.passport;
passportController.usePassport();
return {
rememberme: rememberme,
User: User,
login: login,
logout: logout,
activate_user: activate_user,
get_activation: get_activation,
ensureAuthenticated: ensureAuthenticated,
forceAuthLogin: forceAuthLogin,
loginExtSettings: loginExtSettings,
passport: passport,
passportController: passportController
};
};
module.exports = controller;