Files
OpenNoodl/packages/noodl-parse-dashboard/Parse-Dashboard/Authentication.js
Michael Cartner b9c60b07dc Initial commit
Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com>
Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com>
Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com>
Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com>
Co-Authored-By: Johan  <4934465+joolsus@users.noreply.github.com>
Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com>
Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
2024-01-26 11:52:55 +01:00

147 lines
4.4 KiB
JavaScript

'use strict';
var bcrypt = require('bcryptjs');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
const OTPAuth = require('otpauth')
/**
* Constructor for Authentication class
*
* @class Authentication
* @param {Object[]} validUsers
* @param {boolean} useEncryptedPasswords
*/
function Authentication(validUsers, useEncryptedPasswords, mountPath) {
this.validUsers = validUsers;
this.useEncryptedPasswords = useEncryptedPasswords || false;
this.mountPath = mountPath;
}
function initialize(app, options) {
options = options || {};
var self = this;
passport.use('local', new LocalStrategy(
{passReqToCallback:true},
function(req, username, password, cb) {
var match = self.authenticate({
name: username,
pass: password,
otpCode: req.body.otpCode
});
if (!match.matchingUsername) {
return cb(null, false, { message: JSON.stringify({ text: 'Invalid username or password' }) });
}
if (!match.otpValid) {
return cb(null, false, { message: JSON.stringify({ text: 'Invalid one-time password.', otpLength: match.otpMissingLength || 6}) });
}
if (match.otpMissingLength) {
return cb(null, false, { message: JSON.stringify({ text: 'Please enter your one-time password.', otpLength: match.otpMissingLength || 6 })});
}
cb(null, match.matchingUsername);
})
);
passport.serializeUser(function(username, cb) {
cb(null, username);
});
passport.deserializeUser(function(username, cb) {
var user = self.authenticate({
name: username
}, true);
cb(null, user);
});
var cookieSessionSecret = options.cookieSessionSecret || require('crypto').randomBytes(64).toString('hex');
app.use(require('connect-flash')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('cookie-session')({
key : 'parse_dash',
secret : cookieSessionSecret,
cookie : {
maxAge: (2 * 7 * 24 * 60 * 60 * 1000) // 2 weeks
}
}));
app.use(passport.initialize());
app.use(passport.session());
app.post('/login',
passport.authenticate('local', {
successRedirect: `${self.mountPath}apps`,
failureRedirect: `${self.mountPath}login`,
failureFlash : true
})
);
app.get('/logout', function(req, res){
req.logout();
res.redirect(`${self.mountPath}login`);
});
}
/**
* Authenticates the `userToTest`
*
* @param {Object} userToTest
* @returns {Object} Object with `isAuthenticated` and `appsUserHasAccessTo` properties
*/
function authenticate(userToTest, usernameOnly) {
let appsUserHasAccessTo = null;
let matchingUsername = null;
let isReadOnly = false;
let otpMissingLength = false;
let otpValid = true;
//they provided auth
let isAuthenticated = userToTest &&
//there are configured users
this.validUsers &&
//the provided auth matches one of the users
this.validUsers.find(user => {
let isAuthenticated = false;
let usernameMatches = userToTest.name == user.user;
if (usernameMatches && user.mfa && !usernameOnly) {
if (!userToTest.otpCode) {
otpMissingLength = user.mfaDigits || 6;
} else {
const totp = new OTPAuth.TOTP({
algorithm: user.mfaAlgorithm || 'SHA1',
secret: OTPAuth.Secret.fromBase32(user.mfa),
digits: user.mfaDigits,
period: user.mfaPeriod,
});
const valid = totp.validate({
token: userToTest.otpCode
});
if (valid === null) {
otpValid = false;
otpMissingLength = user.mfaDigits || 6;
}
}
}
let passwordMatches = this.useEncryptedPasswords && !usernameOnly ? bcrypt.compareSync(userToTest.pass, user.pass) : userToTest.pass == user.pass;
if (usernameMatches && (usernameOnly || passwordMatches)) {
isAuthenticated = true;
matchingUsername = user.user;
// User restricted apps
appsUserHasAccessTo = user.apps || null;
isReadOnly = !!user.readOnly; // make it true/false
}
return isAuthenticated;
}) ? true : false;
return {
isAuthenticated,
matchingUsername,
otpMissingLength,
otpValid,
appsUserHasAccessTo,
isReadOnly,
};
}
Authentication.prototype.initialize = initialize;
Authentication.prototype.authenticate = authenticate;
module.exports = Authentication;