var sanitize = require('mongo-sanitize'); var speakeasy = require('speakeasy'); let db = global['requireModule']('database'); module.exports = { path: "/login", /** * login a user * @url /api/login * @method POST * @POST ['email', 'password'] */ post: async (req, res) => { // if user is logged in (existing session); check MFA if(req.session.user) { if(!req.session.user.loggedInFull) { if(!req.body.mfa) { return res.type('json').status(401).end(JSON.stringify({ status: 401, message: [ 'msg.request.data.missing', 'msg.auth.login.failed' ] })); } let mfa = sanitize(req.body.mfa); user = await db.getUser(req.session.user.id); // if database error if(user.err) { // log error while debugging global['logs'].debug(user.err); // login failed because of database error return res.type('json').status(500).end(JSON.stringify({ status: 500, message: [ 'msg.database.error', 'msg.auth.login.failed' ] })); } // no reply (user does not exist) or password is wrong if(!user.reply || user.reply === null || user.reply.length == 0 || user.reply.length > 1) { return res.type('json').status(401).end(JSON.stringify({ status: 401, message: 'msg.auth.login.failed' })); // do login } else { if(req.session.user.login_step+1 <= user.reply.mfa.data.length) { let test = false; switch (req.session.user.login_step_type) { case "TOTP": test = speakeasy.totp.verify({ secret: user.reply.mfa.data[req.session.user.login_step].data, encoding: 'base32', token: mfa }); break; case "HOTP": test = speakeasy.hotp.verify({ secret: user.reply.mfa.data[req.session.user.login_step].data.split("|")[0], counter: user.reply.mfa.data[req.session.user.login_step].data.split("|")[1], encoding: 'base32', token: mfa }); break; } if(test) { if(req.session.user.login_step+1 >= user.reply.mfa.data.length) { req.session.user.loggedInFull = true; delete req.session.user.login_step; delete req.session.user.login_step_type; } else { req.session.user.login_step++; req.session.user.login_step_type = user.reply.mfa.data[req.session.user.login_step].type; } return res.type('json').end(JSON.stringify({ status: 200, message: 'msg.auth.login.mfa', type: 'form' // TODO: types - { form, access_app} })); } else { if(req.session.user.login_step_type == 'HOTP') { let mfaobj = user.reply.mfa; mfaobj[req.session.user.login_step].data = user.reply.mfa[req.session.user.login_step].data.split("|")[0] + (user.reply.mfa[req.session.user.login_step].data.split("|")[1]++); await db.updateUser(user.id, { mfa: mfaobj }); } return res.type('json').status(401).end(JSON.stringify({ status: 401, message: [ 'msg.auth.login.failed', 'msg.auth.login.mfa' ] })); } return res.type('json').end(JSON.stringify({ status: 200, message: 'msg.auth.login.mfa', type: 'form' // TODO: types - { form, access_app} })); } return res.type('json').status(401).end(JSON.stringify({ status: 401, message: [ 'msg.request.data.missing', 'msg.auth.login.failed' ] })); } } else { return res.type('json').end(JSON.stringify({ status: 401, message: 'msg.auth.logout.required' })); } } // check body variables if(!req.body.email || !req.body.password) { return res.type('json').status(401).end(JSON.stringify({ status: 401, message: [ 'msg.request.data.missing', 'msg.auth.login.failed' ] })); } let email = sanitize(req.body.email); let pass = sanitize(req.body.password); // database query: get user by email user = await db.getUser(email); // if database error if(user.err) { // log error while debugging global['logs'].debug(user.err); // login failed because of database error return res.type('json').status(500).end(JSON.stringify({ status: 500, message: [ 'msg.database.error', 'msg.auth.login.failed' ] })); } // no reply (user does not exist) or password is wrong if(!user.reply || user.reply === null || user.reply.length == 0 || user.reply.length > 1 || !global['requireModule']('auth').validateHash(user.reply.passhash, pass)) { return res.type('json').status(401).end(JSON.stringify({ status: 401, message: 'msg.auth.login.failed' })); // do login } else { // add cookies; login // new activity 'action.user.login' // add session data req.session.user = { 'id': user.reply._id, 'group': user.reply.group }; req.session.user.loggedInFull = user.reply.mfa.active == false; // mfa active? if(!req.session.user.loggedInFull) { // mfa is active req.session.user.login_step_type = user.reply.mfa.data[0].type; req.session.user.login_step = 0; return res.type('json').end(JSON.stringify({ status: 200, message: 'msg.auth.login.mfa', type: 'form' // TODO: types - { form, access_app} })); } else { return res.type('json').end(JSON.stringify({ status: 200, message: 'msg.auth.login.successful', type: 'form' // TODO: types - { form, access_app} })); } } } };