diff --git a/config-sample.ini b/config-sample.ini index 84fb29cdd..7b70da5a3 100644 --- a/config-sample.ini +++ b/config-sample.ini @@ -56,7 +56,7 @@ totpEnabled=false # Set the secret key for TOTP authentication # This is a security feature that adds an extra layer of protection to your account -totpSecret= +totpSecret=my-secret-key # Set to true to enable OAuth/OpenID authentication # This will allow users to log in to Trilium using an account from another service, like Google, to verify their identity @@ -64,12 +64,12 @@ ssoEnabled=false # Set the base URL for OAuth/OpenID authentication # This is the URL of the service that will be used to verify the user's identity -oauthBaseUrl= +oauthBaseUrl=http://localhost:8080 # Set the client ID for OAuth/OpenID authentication # This is the ID of the client that will be used to verify the user's identity -oauthClientId= +oauthClientId=my-client-id # Set the client secret for OAuth/OpenID authentication # This is the secret of the client that will be used to verify the user's identity -oauthClientSecret= +oauthClientSecret=my-client-secret diff --git a/src/app.ts b/src/app.ts index 3d6e9cf18..1874c41e2 100644 --- a/src/app.ts +++ b/src/app.ts @@ -14,6 +14,7 @@ import custom from "./routes/custom.js"; import error_handlers from "./routes/error_handlers.js"; import { startScheduledCleanup } from "./services/erase.js"; import sql_init from "./services/sql_init.js"; +import totp from "./services/totp.js"; import oidc from "express-openid-connect"; import openID from "./services/open_id.js"; import { t } from "i18next"; @@ -62,6 +63,9 @@ app.use(`/icon.png`, express.static(path.join(scriptDir, "public/icon.png"))); app.use(sessionParser); app.use(favicon(`${scriptDir}/../images/app-icons/icon.ico`)); +// Check if TOTP is enabled and validate the secret +totp.isTotpEnabled(); + if (openID.checkOpenIDRequirements()) app.use(oidc.auth(openID.generateOAuthConfig())); diff --git a/src/errors/open_id_error.ts b/src/errors/mfa_error.ts similarity index 67% rename from src/errors/open_id_error.ts rename to src/errors/mfa_error.ts index 396598b49..9d1d7e8be 100644 --- a/src/errors/open_id_error.ts +++ b/src/errors/mfa_error.ts @@ -1,4 +1,4 @@ -class OpenIDError { +class MFAError { message: string; constructor(message: string) { @@ -6,4 +6,4 @@ class OpenIDError { } } -export default OpenIDError; \ No newline at end of file +export default MFAError; \ No newline at end of file diff --git a/src/public/app/widgets/type_widgets/options/multi_factor_authentication.ts b/src/public/app/widgets/type_widgets/options/multi_factor_authentication.ts index c85174d5d..6c34170c4 100644 --- a/src/public/app/widgets/type_widgets/options/multi_factor_authentication.ts +++ b/src/public/app/widgets/type_widgets/options/multi_factor_authentication.ts @@ -205,8 +205,11 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { this.$oAuthEnabledCheckbox.prop("checked", result.enabled); if (result.name) this.$UserAccountName.text(result.name); if (result.email) this.$UserAccountEmail.text(result.email); + + this.$envEnabledOAuth.hide(); } else { this.$envEnabledOAuth.text(t("multi_factor_authentication.oauth_enable_description")); + this.$envEnabledOAuth.show(); } }); @@ -215,6 +218,8 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { this.$totpEnabled.prop("checked", result.message); this.$authenticatorCode.prop("disabled", !result.message); this.$generateRecoveryCodeButton.prop("disabled", !result.message); + + this.$envEnabledTOTP.hide(); } else { this.$totpEnabled.prop("checked", false); this.$totpEnabled.prop("disabled", true); @@ -222,6 +227,7 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { this.$generateRecoveryCodeButton.prop("disabled", true); this.$envEnabledTOTP.text(t("multi_factor_authentication.totp_enable_description")); + this.$envEnabledTOTP.show(); } }); this.$protectedSessionTimeout.val(Number(options.protectedSessionTimeout)); diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json index def629313..07fe05780 100644 --- a/src/public/translations/en/translation.json +++ b/src/public/translations/en/translation.json @@ -1314,14 +1314,14 @@ "description": "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. It's like adding an extra lock to your door, making it much harder for anyone else to break in.", "oauth_title": "OAuth/OpenID", "oauth_enabled": "OAuth/OpenID Enabled", - "oauth_enable_description": "Set ssoEnabled in config file to true or set TRILIUM_SSO_ENABLED environment variable to true to enable (Requires restart)", + "oauth_enable_description": "Set ssoEnabled in config file or TRILIUM_SSO_ENABLED environment variable to true to enable (Requires restart)", "oauth_user_account": "User Account:", "oauth_user_email": "User Email:", "oauth_user_not_logged_in": "Not logged in!", "oauth_description": "OpenID is a standardized way to let you log into websites using an account from another service, like Google, to verify your identity.", "totp_title": "Time-based One-Time Password", "totp_enabled": "TOTP Enabled", - "totp_enable_description": "Set totpEnabled in config file to true or set TRILIUM_TOTP_ENABLED environment variable to true to enable (Requires restart)", + "totp_enable_description": "Set totpEnabled in config file or TRILIUM_TOTP_ENABLED environment variable to true to enable (Requires restart)", "totp_description": "TOTP (Time-Based One-Time Password) is a security feature that generates a unique, temporary code which changes every 30 seconds. You use this code, along with your password to log into your account, making it much harder for anyone else to access it.", "totp_secret_title": "Generate TOTP Secret", "totp_secret_description": "TOTP Secret Key", diff --git a/src/routes/api/totp.ts b/src/routes/api/totp.ts index e98c8daba..7315b664e 100644 --- a/src/routes/api/totp.ts +++ b/src/routes/api/totp.ts @@ -2,7 +2,7 @@ import { generateSecret } from 'time2fa'; import config from '../../services/config.js'; function generateTOTPSecret() { - return { success: 'true', message: generateSecret() }; + return { success: true, message: generateSecret() }; } function getTotpEnabled() { diff --git a/src/services/encryption/open_id_encryption.ts b/src/services/encryption/open_id_encryption.ts index 76b1812b7..350b9bdba 100644 --- a/src/services/encryption/open_id_encryption.ts +++ b/src/services/encryption/open_id_encryption.ts @@ -3,7 +3,7 @@ import utils from "../utils.js"; import dataEncryptionService from "./data_encryption.js"; import sql from "../sql.js"; import sqlInit from "../sql_init.js"; -import OpenIDError from "../../errors/open_id_error.js"; +import OpenIDError from "../../errors/mfa_error.js"; function saveUser(subjectIdentifier: string, name: string, email: string) { if (isUserSaved()) return false; @@ -55,7 +55,7 @@ function isUserSaved() { const isSaved = sql.getValue("SELECT isSetup FROM user_data;"); return isSaved === "true" ? true : false; } - + function verifyOpenIDSubjectIdentifier(subjectIdentifier: string) { if (!sqlInit.isDbInitialized()) { throw new OpenIDError("Database not initialized!"); @@ -142,4 +142,4 @@ export default { setDataKey, saveUser, isSubjectIdentifierSaved, -}; \ No newline at end of file +}; diff --git a/src/services/open_id.ts b/src/services/open_id.ts index 2df9cd577..6515fa5ec 100644 --- a/src/services/open_id.ts +++ b/src/services/open_id.ts @@ -1,4 +1,4 @@ -import OpenIDError from "../errors/open_id_error.js"; +import OpenIDError from "../errors/mfa_error.js"; import type { NextFunction, Request, Response } from "express"; import openIDEncryption from "./encryption/open_id_encryption.js"; import sqlInit from "./sql_init.js"; @@ -37,18 +37,20 @@ function clearSavedUser() { } function checkOpenIDRequirements() { - if (config.MultiFactorAuthentication.totpEnabled) { - throw new OpenIDError("Cannot enable both OpenID and TOTP!"); - } + if (config.MultiFactorAuthentication.ssoEnabled) { + if (config.MultiFactorAuthentication.totpEnabled) { + throw new OpenIDError("Cannot enable both OpenID and TOTP!"); + } - if (config.MultiFactorAuthentication.oauthBaseUrl === "") { - throw new OpenIDError("oauthBaseUrl is undefined!"); - } - if (config.MultiFactorAuthentication.oauthClientId === "") { - throw new OpenIDError("oauthClientId is undefined!"); - } - if (config.MultiFactorAuthentication.oauthClientSecret === "") { - throw new OpenIDError("oauthClientSecret is undefined!"); + if (config.MultiFactorAuthentication.oauthBaseUrl === "") { + throw new OpenIDError("oauthBaseUrl is undefined!"); + } + if (config.MultiFactorAuthentication.oauthClientId === "") { + throw new OpenIDError("oauthClientId is undefined!"); + } + if (config.MultiFactorAuthentication.oauthClientSecret === "") { + throw new OpenIDError("oauthClientSecret is undefined!"); + } } return config.MultiFactorAuthentication.ssoEnabled; @@ -146,4 +148,4 @@ export default { checkOpenIDRequirements, isTokenValid, isUserSaved, -}; \ No newline at end of file +}; diff --git a/src/services/totp.ts b/src/services/totp.ts index 71580fb92..f6fecf89c 100644 --- a/src/services/totp.ts +++ b/src/services/totp.ts @@ -1,37 +1,30 @@ -'use strict'; +import { Totp } from 'time2fa'; +import config from './config.js'; +import MFAError from '../errors/mfa_error.js'; -import {Totp} from 'time2fa'; function isTotpEnabled() { - if (process.env.TOTP_ENABLED === undefined) { - return false; + if (config.MultiFactorAuthentication.totpEnabled && config.MultiFactorAuthentication.totpSecret === "") { + throw new MFAError("TOTP secret is not set!"); } - if (process.env.TOTP_SECRET === undefined) { - return false; - } - if (process.env.TOTP_ENABLED.toLocaleLowerCase() !== 'true') { - return false; - } - - return true; + return config.MultiFactorAuthentication.totpEnabled; } function getTotpSecret() { - return process.env.TOTP_SECRET; + return config.MultiFactorAuthentication.totpSecret; } function checkForTotSecret() { - if (process.env.TOTP_SECRET !== undefined) return true; - else return false; + return config.MultiFactorAuthentication.totpSecret === "" ? false : true; } function validateTOTP(guessedPasscode: string) { - if (process.env.TOTP_SECRET === undefined) return false; + if (config.MultiFactorAuthentication.totpSecret === "") return false; try { const valid = Totp.validate({ passcode: guessedPasscode, - secret: process.env.TOTP_SECRET.trim() + secret: config.MultiFactorAuthentication.totpSecret.trim() }); return valid; } catch (e) { @@ -41,7 +34,7 @@ function validateTOTP(guessedPasscode: string) { export default { isTotpEnabled, - getTotpSecret, - checkForTotSecret, + getTotpSecret, + checkForTotSecret, validateTOTP }; \ No newline at end of file diff --git a/src/views/login.ejs b/src/views/login.ejs index 72304d5ed..3c2f0a5a8 100644 --- a/src/views/login.ejs +++ b/src/views/login.ejs @@ -33,9 +33,9 @@ <% if( totpEnabled ) { %>
- +
-