mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-08-10 10:22:29 +08:00
TOTP working
This commit is contained in:
parent
9c748f326a
commit
e232c6634e
@ -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",
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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)"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
|
19
src/services/environment_variable_loader.ts
Normal file
19
src/services/environment_variable_loader.ts
Normal 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
|
||||
}
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user