First pass formatting with Prettier
* Added .prettierrc.json * Added .prettierignore * Formatted
This commit is contained in:
@@ -2,67 +2,61 @@
|
||||
'use strict';
|
||||
|
||||
// ENiGMA½
|
||||
const UserProps = require('./user_property.js');
|
||||
const {
|
||||
Errors,
|
||||
ErrorReasons,
|
||||
} = require('./enig_error.js');
|
||||
const User = require('./user.js');
|
||||
const {
|
||||
recordLogin,
|
||||
transformLoginError,
|
||||
} = require('./user_login.js');
|
||||
const Config = require('./config.js').get;
|
||||
const UserProps = require('./user_property.js');
|
||||
const { Errors, ErrorReasons } = require('./enig_error.js');
|
||||
const User = require('./user.js');
|
||||
const { recordLogin, transformLoginError } = require('./user_login.js');
|
||||
const Config = require('./config.js').get;
|
||||
|
||||
// deps
|
||||
const _ = require('lodash');
|
||||
const crypto = require('crypto');
|
||||
const qrGen = require('qrcode-generator');
|
||||
const _ = require('lodash');
|
||||
const crypto = require('crypto');
|
||||
const qrGen = require('qrcode-generator');
|
||||
|
||||
exports.prepareOTP = prepareOTP;
|
||||
exports.createBackupCodes = createBackupCodes;
|
||||
exports.createQRCode = createQRCode;
|
||||
exports.otpFromType = otpFromType;
|
||||
exports.loginFactor2_OTP = loginFactor2_OTP;
|
||||
exports.prepareOTP = prepareOTP;
|
||||
exports.createBackupCodes = createBackupCodes;
|
||||
exports.createQRCode = createQRCode;
|
||||
exports.otpFromType = otpFromType;
|
||||
exports.loginFactor2_OTP = loginFactor2_OTP;
|
||||
|
||||
const OTPTypes = exports.OTPTypes = {
|
||||
RFC6238_TOTP : 'rfc6238_TOTP', // Time-Based, SHA-512
|
||||
RFC4266_HOTP : 'rfc4266_HOTP', // HMAC-Based, SHA-512
|
||||
GoogleAuthenticator : 'googleAuth', // Google Authenticator is basically TOTP + quirks
|
||||
};
|
||||
const OTPTypes = (exports.OTPTypes = {
|
||||
RFC6238_TOTP: 'rfc6238_TOTP', // Time-Based, SHA-512
|
||||
RFC4266_HOTP: 'rfc4266_HOTP', // HMAC-Based, SHA-512
|
||||
GoogleAuthenticator: 'googleAuth', // Google Authenticator is basically TOTP + quirks
|
||||
});
|
||||
|
||||
function otpFromType(otpType) {
|
||||
try {
|
||||
return {
|
||||
[ OTPTypes.RFC6238_TOTP ] : () => {
|
||||
[OTPTypes.RFC6238_TOTP]: () => {
|
||||
const totp = require('otplib/totp');
|
||||
totp.options = { crypto, algorithm : 'sha256' };
|
||||
totp.options = { crypto, algorithm: 'sha256' };
|
||||
return totp;
|
||||
},
|
||||
[ OTPTypes.RFC4266_HOTP ] : () => {
|
||||
[OTPTypes.RFC4266_HOTP]: () => {
|
||||
const hotp = require('otplib/hotp');
|
||||
hotp.options = { crypto, algorithm : 'sha256' };
|
||||
hotp.options = { crypto, algorithm: 'sha256' };
|
||||
return hotp;
|
||||
},
|
||||
[ OTPTypes.GoogleAuthenticator ] : () => {
|
||||
[OTPTypes.GoogleAuthenticator]: () => {
|
||||
const googleAuth = require('otplib/authenticator');
|
||||
googleAuth.options = { crypto };
|
||||
return googleAuth;
|
||||
},
|
||||
}[otpType]();
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
function generateOTPBackupCode() {
|
||||
const consonants = 'bdfghjklmnprstvz'.split('');
|
||||
const vowels = 'aiou'.split('');
|
||||
const vowels = 'aiou'.split('');
|
||||
|
||||
const bits = [];
|
||||
const rng = crypto.randomBytes(4);
|
||||
|
||||
for(let i = 0; i < rng.length / 2; ++i) {
|
||||
for (let i = 0; i < rng.length / 2; ++i) {
|
||||
const n = rng.readUInt16BE(i * 2);
|
||||
|
||||
const c1 = n & 0x0f;
|
||||
@@ -71,13 +65,11 @@ function generateOTPBackupCode() {
|
||||
const v2 = (n >> 10) & 0x03;
|
||||
const c3 = (n >> 12) & 0x0f;
|
||||
|
||||
bits.push([
|
||||
consonants[c1],
|
||||
vowels[v1],
|
||||
consonants[c2],
|
||||
vowels[v2],
|
||||
consonants[c3],
|
||||
].join(''));
|
||||
bits.push(
|
||||
[consonants[c1], vowels[v1], consonants[c2], vowels[v2], consonants[c3]].join(
|
||||
''
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return bits.join('-');
|
||||
@@ -89,12 +81,15 @@ function createBackupCodes() {
|
||||
}
|
||||
|
||||
function validateAndConsumeBackupCode(user, token, cb) {
|
||||
try
|
||||
{
|
||||
let validCodes = JSON.parse(user.getProperty(UserProps.AuthFactor2OTPBackupCodes));
|
||||
try {
|
||||
let validCodes = JSON.parse(
|
||||
user.getProperty(UserProps.AuthFactor2OTPBackupCodes)
|
||||
);
|
||||
const matchingCode = validCodes.find(c => c === token);
|
||||
if(!matchingCode) {
|
||||
return cb(Errors.BadLogin('Invalid OTP value supplied', ErrorReasons.Invalid2FA));
|
||||
if (!matchingCode) {
|
||||
return cb(
|
||||
Errors.BadLogin('Invalid OTP value supplied', ErrorReasons.Invalid2FA)
|
||||
);
|
||||
}
|
||||
|
||||
// We're consuming a match - remove it from available backup codes
|
||||
@@ -103,68 +98,73 @@ function validateAndConsumeBackupCode(user, token, cb) {
|
||||
user.persistProperty(UserProps.AuthFactor2OTPBackupCodes, validCodes, err => {
|
||||
return cb(err);
|
||||
});
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
return cb(e);
|
||||
}
|
||||
}
|
||||
|
||||
function createQRCode(otp, options, secret) {
|
||||
try {
|
||||
const uri = otp.keyuri(options.username || 'user', Config().general.boardName, secret);
|
||||
const uri = otp.keyuri(
|
||||
options.username || 'user',
|
||||
Config().general.boardName,
|
||||
secret
|
||||
);
|
||||
const qrCode = qrGen(0, 'L');
|
||||
qrCode.addData(uri);
|
||||
qrCode.make();
|
||||
|
||||
options.qrType = options.qrType || 'ascii';
|
||||
return {
|
||||
ascii : qrCode.createASCII,
|
||||
data : qrCode.createDataURL,
|
||||
img : qrCode.createImgTag,
|
||||
svg : qrCode.createSvgTag,
|
||||
ascii: qrCode.createASCII,
|
||||
data: qrCode.createDataURL,
|
||||
img: qrCode.createImgTag,
|
||||
svg: qrCode.createSvgTag,
|
||||
}[options.qrType](options.cellSize);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function prepareOTP(otpType, options, cb) {
|
||||
if(!_.isFunction(cb)) {
|
||||
if (!_.isFunction(cb)) {
|
||||
cb = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
const otp = otpFromType(otpType);
|
||||
if(!otp) {
|
||||
if (!otp) {
|
||||
return cb(Errors.Invalid(`Unknown OTP type: ${otpType}`));
|
||||
}
|
||||
|
||||
const secret = OTPTypes.GoogleAuthenticator === otpType ?
|
||||
otp.generateSecret() :
|
||||
crypto.randomBytes(64).toString('base64').substr(0, 32);
|
||||
const secret =
|
||||
OTPTypes.GoogleAuthenticator === otpType
|
||||
? otp.generateSecret()
|
||||
: crypto.randomBytes(64).toString('base64').substr(0, 32);
|
||||
|
||||
const qr = createQRCode(otp, options, secret);
|
||||
|
||||
return cb(null, { secret, qr } );
|
||||
return cb(null, { secret, qr });
|
||||
}
|
||||
|
||||
function loginFactor2_OTP(client, token, cb) {
|
||||
if(client.user.authFactor < User.AuthFactors.Factor1) {
|
||||
if (client.user.authFactor < User.AuthFactors.Factor1) {
|
||||
return cb(Errors.AccessDenied('OTP requires prior authentication factor 1'));
|
||||
}
|
||||
|
||||
const otpType = client.user.getProperty(UserProps.AuthFactor2OTP);
|
||||
const otp = otpFromType(otpType);
|
||||
const otp = otpFromType(otpType);
|
||||
|
||||
if(!otp) {
|
||||
if (!otp) {
|
||||
return cb(Errors.Invalid(`Unknown OTP type: ${otpType}`));
|
||||
}
|
||||
|
||||
const secret = client.user.getProperty(UserProps.AuthFactor2OTPSecret);
|
||||
if(!secret) {
|
||||
if (!secret) {
|
||||
return cb(Errors.Invalid('Missing OTP secret'));
|
||||
}
|
||||
|
||||
const valid = otp.verify( { token, secret } );
|
||||
const valid = otp.verify({ token, secret });
|
||||
|
||||
const allowLogin = () => {
|
||||
client.user.authFactor = User.AuthFactors.Factor2;
|
||||
@@ -172,13 +172,13 @@ function loginFactor2_OTP(client, token, cb) {
|
||||
return recordLogin(client, cb);
|
||||
};
|
||||
|
||||
if(valid) {
|
||||
if (valid) {
|
||||
return allowLogin();
|
||||
}
|
||||
|
||||
// maybe they punched in a backup code?
|
||||
validateAndConsumeBackupCode(client.user, token, err => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return cb(transformLoginError(err, client, client.user.username));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user