TOTP working

This commit is contained in:
chesspro13 2024-09-07 11:41:54 -07:00
parent 9c748f326a
commit e232c6634e
No known key found for this signature in database
GPG Key ID: 5FEAE94D298066E5
7 changed files with 77 additions and 36 deletions

View File

@ -21,6 +21,7 @@
"type": "module",
"scripts": {
"start-server": "cross-env TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/www.ts",
"start-server-mfa": "cross-env TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon --env-file=.env src/www.ts",
"start-server-safe": "cross-env TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/www.ts",
"start-server-no-dir": "cross-env TRILIUM_SAFE_MODE=1 TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/www.ts",
"start-test-server": "npm run switch-server; rimraf ./data-test; cross-env TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data-test TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev TRILIUM_PORT=9999 ts-node src/www.ts",
@ -69,6 +70,7 @@
"dayjs": "^1.11.12",
"dayjs-plugin-utc": "0.1.2",
"debounce": "^2.1.0",
"dotenv": "^16.4.5",
"ejs": "^3.1.10",
"electron-debug": "3.2.0",
"electron-dl": "3.5.2",

View File

@ -16,6 +16,7 @@ import { startScheduledCleanup } from "./services/erase.js";
import sql_init from "./services/sql_init.js";
import oidc from "express-openid-connect";
import openID from "./services/open_id.js";
import * as dotenv from "dotenv";
await import('./services/handlers.js');
await import('./becca/becca_loader.js');
@ -24,6 +25,9 @@ const app = express();
const scriptDir = dirname(fileURLToPath(import.meta.url));
// Configure environment variables
dotenv.config();
// Initialize DB
sql_init.initializeDb();

View File

@ -10,7 +10,7 @@ const TPL = `
Multi-Factor Authentication (MFA) adds an extra layer of security to your account. Instead
of just entering a password to log in, MFA requires you to provide one or more additional
pieces of evidence to verify your identity. This way, even if someone gets hold of your
password, they still can't access your account without the second piece of information.
password, they still ca TOTP_ENABLED is not set in environment variable. Requires restart.n't access your account without the second piece of information.
It's like adding an extra lock to your door, making it much harder for anyone else to
break in.</i>
</div>
@ -51,7 +51,7 @@ const TPL = `
<div>
<span class="totp-secret" > TOTP Secret Key </span>
<br>
<button class="regenerate-totp" disabled="true"> Regenerate TOTP Secret </button>
<button class="regenerate-totp"> Regenerate TOTP Secret </button>
</div>
<br>
<h4> Single Sign-on Recovery Keys </h4>
@ -117,9 +117,10 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
for (let i = 0; i < 8; i++)
this.$recoveryKeys.push(this.$widget.find(".key_" + i));
this.$totpEnabled.on("change", async () => {
this.updateSecret();
});
// Depricated. Will use .env to control.
// this.$totpEnabled.on("change", async () => {
// this.updateSecret();
// });
this.$oAuthEnabledCheckbox.on("change", async () => {
this.updateOAuthStatus();
@ -159,15 +160,16 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
this.displayRecoveryKeys();
}
async updateSecret() {
if (this.$totpEnabled.prop("checked")) {
server.post("totp/enable");
// Depricated. Will use .env to control.
// async updateSecret() {
// if (this.$totpEnabled.prop("checked")) {
// server.post("totp/enable");
}
else {
server.post("totp/disable");
}
}
// }
// else {
// server.post("totp/disable");
// }
// }
async updateOAuthStatus() {
if (this.$oAuthEnabledCheckbox.prop("checked")){
@ -248,12 +250,11 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
// });
server.get("totp/status").then((result) => {
console.log(result);
if (result.enabled)
if (result.success) {
this.$totpEnabled.prop("checked", result.message);
this.$totpSecretInput.prop("disabled", !result.message);
this.$totpSecret.prop("disapbled", !result.message);
this.$regenerateTotpButton.prop("disabled", !result.message);
this.$authenticatorCode.prop("disabled", !result.message);
this.$generateRecoveryCodeButton.prop("disabled", !result.message);
} else {
@ -263,13 +264,11 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
this.$totpEnabled.prop("checked", false);
this.$totpEnabled.prop("disabled", true);
this.$totpSecretInput.prop("disabled", true);
this.$totpSecret.prop("disapbled", true);
this.$regenerateTotpButton.prop("disabled", true);
this.$authenticatorCode.prop("disabled", true);
this.$generateRecoveryCodeButton.prop("disabled", true);
this.$envEnabledTOTP.text(
"TOTP_ENABLED is not set in environment variable. Requires restart."
"TOTP_ENABLED is set as environment variable to enable (Requires restart)"
);
}
});

View File

@ -17,8 +17,9 @@ function getTotpEnabled() {
}
function getTOTPStatus() {
const totpEnabled = options.getOptionBool('totpEnabled');
return {success: 'true', message: totpEnabled, enabled: getTotpEnabled()};
// const totpEnabled = options.getOptionBool('totpEnabled');
const totpEnabled = getTotpEnabled();
return {success: true, message: totpEnabled, enabled: getTotpEnabled()};
}
function enableTOTP() {

View File

@ -22,7 +22,7 @@ function loginPage(req: Request, res: Response) {
} else {
res.render('login', {
failedAuth: false,
totpEnabled: optionService.getOptionBool('totpEnabled') && totp.checkForTotSecret(),
totpEnabled: totp.isTotpEnabled(),
assetPath: assetPath,
appPath: appPath,
});
@ -71,17 +71,19 @@ function login(req: AppRequest, res: Response) {
const guessedPassword = req.body.password;
const guessedTotp = req.body.token;
if (totp.isTotpEnabled()){
if (!verifyTOTP(guessedTotp)) {
sendLoginError(req, res);
return;
}
}
if (verifyPassword(guessedPassword)) {
if (!verifyPassword(guessedPassword)) {
sendLoginError(req, res);
return;
}
if (optionService.getOptionBool('totpEnabled') && totp.checkForTotSecret())
if (!verifyTOTP(guessedTotp)) {
sendLoginError(req, res);
return;
}
}
const rememberMe = req.body.rememberMe;
req.session.regenerate(() => {
@ -96,13 +98,7 @@ function login(req: AppRequest, res: Response) {
});
}
else {
// note that logged IP address is usually meaningless since the traffic should come from a reverse proxy
log.info(`WARNING: Wrong password from ${req.ip}, rejecting.`);
res.status(401).render('login', {
failedAuth: true,
assetPath: assetPath
});
sendLoginError(req, res);
}
}
@ -124,7 +120,11 @@ function verifyPassword(guessedPassword: string) {
function sendLoginError(req: AppRequest, res: Response) {
// note that logged IP address is usually meaningless since the traffic should come from a reverse proxy
log.info(`WARNING: Wrong password or TOTP from ${req.ip}, rejecting.`);
if ( totp.isTotpEnabled( )){
log.info(`WARNING: Wrong password or TOTP from ${req.ip}, rejecting.`);
}else{
log.info(`WARNING: Wrong password from ${req.ip}, rejecting.`);
}
res.status(401).render('login', {
failedAuth: true,

View File

@ -0,0 +1,19 @@
import options from '../services/options.js';
function loadEnvironmentVariables(){
if (process.env.TOTP_ENABLED === undefined) {
options.setOption("totpEnabled", false);
return false;
}
if (process.env.TOTP_ENABLED.toLocaleLowerCase() !== 'true') {
options.setOption("totpEnabled", false);
return false;
}
options.setOption("totpEnabled", true);
}
export default {
loadEnvironmentVariables
}

View File

@ -2,6 +2,21 @@
import {Totp} from 'time2fa';
function isTotpEnabled() {
console.log("Reading ENV: " + process.env.TOTP_ENABLED );
if (process.env.TOTP_ENABLED === undefined) {
return false;
}
if (process.env.TOTP_SECRET === undefined) {
return false;
}
if (process.env.TOTP_ENABLED.toLocaleLowerCase() !== 'true') {
return false;
}
return true;
}
function getTotpSecret() {
return process.env.TOTP_SECRET;
}
@ -26,6 +41,7 @@ function validateTOTP(guessedPasscode: string) {
}
export default {
isTotpEnabled,
getTotpSecret,
checkForTotSecret,
validateTOTP