mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 11:39:01 +01:00 
			
		
		
		
	TOTP working
This commit is contained in:
		
							parent
							
								
									9c748f326a
								
							
						
					
					
						commit
						e232c6634e
					
				| @ -21,6 +21,7 @@ | |||||||
|   "type": "module", |   "type": "module", | ||||||
|   "scripts": { |   "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": "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-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-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", |     "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": "^1.11.12", | ||||||
|     "dayjs-plugin-utc": "0.1.2", |     "dayjs-plugin-utc": "0.1.2", | ||||||
|     "debounce": "^2.1.0", |     "debounce": "^2.1.0", | ||||||
|  |     "dotenv": "^16.4.5", | ||||||
|     "ejs": "^3.1.10", |     "ejs": "^3.1.10", | ||||||
|     "electron-debug": "3.2.0", |     "electron-debug": "3.2.0", | ||||||
|     "electron-dl": "3.5.2", |     "electron-dl": "3.5.2", | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ import { startScheduledCleanup } from "./services/erase.js"; | |||||||
| import sql_init from "./services/sql_init.js"; | import sql_init from "./services/sql_init.js"; | ||||||
| import oidc from "express-openid-connect"; | import oidc from "express-openid-connect"; | ||||||
| import openID from "./services/open_id.js"; | import openID from "./services/open_id.js"; | ||||||
|  | import * as dotenv from "dotenv"; | ||||||
| 
 | 
 | ||||||
| await import('./services/handlers.js'); | await import('./services/handlers.js'); | ||||||
| await import('./becca/becca_loader.js'); | await import('./becca/becca_loader.js'); | ||||||
| @ -24,6 +25,9 @@ const app = express(); | |||||||
| 
 | 
 | ||||||
| const scriptDir = dirname(fileURLToPath(import.meta.url)); | const scriptDir = dirname(fileURLToPath(import.meta.url)); | ||||||
| 
 | 
 | ||||||
|  | // Configure environment variables
 | ||||||
|  | dotenv.config(); | ||||||
|  | 
 | ||||||
| // Initialize DB
 | // Initialize DB
 | ||||||
| sql_init.initializeDb(); | sql_init.initializeDb(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ const TPL = ` | |||||||
|             Multi-Factor Authentication (MFA) adds an extra layer of security to your account. Instead |             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  |              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  |              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  |              It's like adding an extra lock to your door, making it much harder for anyone else to  | ||||||
|              break in.</i> |              break in.</i> | ||||||
|     </div> |     </div> | ||||||
| @ -51,7 +51,7 @@ const TPL = ` | |||||||
|     <div> |     <div> | ||||||
|         <span class="totp-secret" > TOTP Secret Key </span> |         <span class="totp-secret" > TOTP Secret Key </span> | ||||||
|         <br> |         <br> | ||||||
|         <button class="regenerate-totp" disabled="true"> Regenerate TOTP Secret </button> |         <button class="regenerate-totp"> Regenerate TOTP Secret </button> | ||||||
|     </div> |     </div> | ||||||
|     <br> |     <br> | ||||||
|     <h4> Single Sign-on Recovery Keys </h4> |     <h4> Single Sign-on Recovery Keys </h4> | ||||||
| @ -117,9 +117,10 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { | |||||||
|     for (let i = 0; i < 8; i++) |     for (let i = 0; i < 8; i++) | ||||||
|       this.$recoveryKeys.push(this.$widget.find(".key_" + i)); |       this.$recoveryKeys.push(this.$widget.find(".key_" + i)); | ||||||
| 
 | 
 | ||||||
|     this.$totpEnabled.on("change", async () => { |     // Depricated. Will use .env to control.
 | ||||||
|       this.updateSecret(); |     // this.$totpEnabled.on("change", async () => {
 | ||||||
|     }); |     //   this.updateSecret();
 | ||||||
|  |     // });
 | ||||||
| 
 | 
 | ||||||
|     this.$oAuthEnabledCheckbox.on("change", async () => { |     this.$oAuthEnabledCheckbox.on("change", async () => { | ||||||
|       this.updateOAuthStatus(); |       this.updateOAuthStatus(); | ||||||
| @ -159,15 +160,16 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { | |||||||
|     this.displayRecoveryKeys(); |     this.displayRecoveryKeys(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async updateSecret() { |   // Depricated. Will use .env to control.
 | ||||||
|     if (this.$totpEnabled.prop("checked")) { |   // async updateSecret() {
 | ||||||
|       server.post("totp/enable"); |   //   if (this.$totpEnabled.prop("checked")) {
 | ||||||
|  |   //     server.post("totp/enable");
 | ||||||
| 
 | 
 | ||||||
|     } |   //   }
 | ||||||
|     else { |   //   else {
 | ||||||
|       server.post("totp/disable"); |   //     server.post("totp/disable");
 | ||||||
|     } |   //   }
 | ||||||
|   } |   // }
 | ||||||
| 
 | 
 | ||||||
|   async updateOAuthStatus() { |   async updateOAuthStatus() { | ||||||
|     if (this.$oAuthEnabledCheckbox.prop("checked")){ |     if (this.$oAuthEnabledCheckbox.prop("checked")){ | ||||||
| @ -248,12 +250,11 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { | |||||||
|     // });
 |     // });
 | ||||||
| 
 | 
 | ||||||
|     server.get("totp/status").then((result) => { |     server.get("totp/status").then((result) => { | ||||||
|  |       console.log(result); | ||||||
|       if (result.enabled) |       if (result.enabled) | ||||||
|         if (result.success) { |         if (result.success) { | ||||||
|           this.$totpEnabled.prop("checked", result.message); |           this.$totpEnabled.prop("checked", result.message); | ||||||
|           this.$totpSecretInput.prop("disabled", !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.$authenticatorCode.prop("disabled", !result.message); | ||||||
|           this.$generateRecoveryCodeButton.prop("disabled", !result.message); |           this.$generateRecoveryCodeButton.prop("disabled", !result.message); | ||||||
|         } else { |         } else { | ||||||
| @ -263,13 +264,11 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget { | |||||||
|         this.$totpEnabled.prop("checked", false); |         this.$totpEnabled.prop("checked", false); | ||||||
|         this.$totpEnabled.prop("disabled", true); |         this.$totpEnabled.prop("disabled", true); | ||||||
|         this.$totpSecretInput.prop("disabled", true); |         this.$totpSecretInput.prop("disabled", true); | ||||||
|         this.$totpSecret.prop("disapbled", true); |  | ||||||
|         this.$regenerateTotpButton.prop("disabled", true); |  | ||||||
|         this.$authenticatorCode.prop("disabled", true); |         this.$authenticatorCode.prop("disabled", true); | ||||||
|         this.$generateRecoveryCodeButton.prop("disabled", true); |         this.$generateRecoveryCodeButton.prop("disabled", true); | ||||||
| 
 | 
 | ||||||
|         this.$envEnabledTOTP.text( |         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() { | function getTOTPStatus() { | ||||||
|     const totpEnabled = options.getOptionBool('totpEnabled'); |     // const totpEnabled = options.getOptionBool('totpEnabled');
 | ||||||
|     return {success: 'true', message: totpEnabled, enabled: getTotpEnabled()}; |     const totpEnabled = getTotpEnabled(); | ||||||
|  |     return {success: true, message: totpEnabled, enabled: getTotpEnabled()}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function enableTOTP() { | function enableTOTP() { | ||||||
|  | |||||||
| @ -22,7 +22,7 @@ function loginPage(req: Request, res: Response) { | |||||||
|     } else { |     } else { | ||||||
|       res.render('login', { |       res.render('login', { | ||||||
|         failedAuth: false, |         failedAuth: false, | ||||||
|         totpEnabled: optionService.getOptionBool('totpEnabled') && totp.checkForTotSecret(), |         totpEnabled: totp.isTotpEnabled(), | ||||||
|         assetPath: assetPath, |         assetPath: assetPath, | ||||||
|         appPath: appPath, |         appPath: appPath, | ||||||
|       }); |       }); | ||||||
| @ -71,17 +71,19 @@ function login(req: AppRequest, res: Response) { | |||||||
|     const guessedPassword = req.body.password; |     const guessedPassword = req.body.password; | ||||||
|     const guessedTotp = req.body.token; |     const guessedTotp = req.body.token; | ||||||
| 
 | 
 | ||||||
|  |     if (totp.isTotpEnabled()){ | ||||||
|  |         if (!verifyTOTP(guessedTotp)) { | ||||||
|  |           sendLoginError(req, res); | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (verifyPassword(guessedPassword)) { |     if (verifyPassword(guessedPassword)) { | ||||||
|         if (!verifyPassword(guessedPassword)) { |         if (!verifyPassword(guessedPassword)) { | ||||||
|             sendLoginError(req, res); |             sendLoginError(req, res); | ||||||
|             return; |             return; | ||||||
|           } |         } | ||||||
|        |              | ||||||
|           if (optionService.getOptionBool('totpEnabled') && totp.checkForTotSecret()) |  | ||||||
|             if (!verifyTOTP(guessedTotp)) { |  | ||||||
|               sendLoginError(req, res); |  | ||||||
|               return; |  | ||||||
|             } |  | ||||||
|         const rememberMe = req.body.rememberMe; |         const rememberMe = req.body.rememberMe; | ||||||
| 
 | 
 | ||||||
|         req.session.regenerate(() => { |         req.session.regenerate(() => { | ||||||
| @ -96,13 +98,7 @@ function login(req: AppRequest, res: Response) { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         // note that logged IP address is usually meaningless since the traffic should come from a reverse proxy
 |         sendLoginError(req, res); | ||||||
|         log.info(`WARNING: Wrong password from ${req.ip}, rejecting.`); |  | ||||||
| 
 |  | ||||||
|         res.status(401).render('login', { |  | ||||||
|             failedAuth: true, |  | ||||||
|             assetPath: assetPath |  | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -124,7 +120,11 @@ function verifyPassword(guessedPassword: string) { | |||||||
| 
 | 
 | ||||||
| function sendLoginError(req: AppRequest, res: Response) { | function sendLoginError(req: AppRequest, res: Response) { | ||||||
|     // note that logged IP address is usually meaningless since the traffic should come from a reverse proxy
 |     // 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', { |     res.status(401).render('login', { | ||||||
|       failedAuth: true, |       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'; | 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() { | function getTotpSecret() { | ||||||
|     return process.env.TOTP_SECRET; |     return process.env.TOTP_SECRET; | ||||||
| } | } | ||||||
| @ -26,6 +41,7 @@ function validateTOTP(guessedPasscode: string) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default { | export default { | ||||||
|  |     isTotpEnabled, | ||||||
|     getTotpSecret,  |     getTotpSecret,  | ||||||
|     checkForTotSecret,  |     checkForTotSecret,  | ||||||
|     validateTOTP |     validateTOTP | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 chesspro13
						chesspro13