Started integrating totp into startup sequance

This commit is contained in:
Chesspro13 2024-05-02 16:06:54 -07:00
parent 3fb4d95fd7
commit baca395c0a
8 changed files with 152 additions and 57 deletions

View File

@ -17,6 +17,26 @@ const TPL = `
Use TOTP (Time-based One-Time Password) to safeguard your data in this application because it adds an additional layer of security by generating unique passcodes that expire quickly, making it harder for unauthorized access. TOTP also reduces the risk of account compromise through common threats like phishing attacks or password breaches. Use TOTP (Time-based One-Time Password) to safeguard your data in this application because it adds an additional layer of security by generating unique passcodes that expire quickly, making it harder for unauthorized access. TOTP also reduces the risk of account compromise through common threats like phishing attacks or password breaches.
</div> </div>
<br>
<h4>TOTP Settings</h4>
<div>
<label>
Enable TOTP
</label>
<input type="checkbox" class="totp-enabled" />
</div>
<br>
<div class="options-section">
<label>
TOTP Secret
</label>
<input class="totp-secret-input form-control" disabled="true" type="text">
<button class="save-totp" disabled="true"> Save TOTP Secret </button>
</div>
<br>
<h4> Generate TOTP Secret </h4>
<span class="totp-secret" > </span> <span class="totp-secret" > </span>
<br> <br>
<button class="regenerate-totp"> Regenerate TOTP Secret </button> <button class="regenerate-totp"> Regenerate TOTP Secret </button>
@ -28,7 +48,10 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
this.$mfaHeadding = this.$widget.find(".mfa-heading"); this.$mfaHeadding = this.$widget.find(".mfa-heading");
this.$regenerateTotpButton = this.$widget.find(".regenerate-totp"); this.$regenerateTotpButton = this.$widget.find(".regenerate-totp");
this.$totpEnabled = this.$widget.find(".totp-enabled");
this.$totpSecret = this.$widget.find(".totp-secret"); this.$totpSecret = this.$widget.find(".totp-secret");
this.$totpSecretInput = this.$widget.find(".totp-secret-input");
this.$saveTotpButton = this.$widget.find(".save-totp");
this.$mfaHeadding.text("Multi-Factor Authentication"); this.$mfaHeadding.text("Multi-Factor Authentication");
this.generateKey(); this.generateKey();
@ -36,6 +59,10 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
// var gen = require("speakeasy"); // var gen = require("speakeasy");
// toastService.showMessage("***REMOVED***"); // toastService.showMessage("***REMOVED***");
this.$totpEnabled.on("change", async () => {
this.updateCheckboxOption("totpEnabled", this.$totpEnabled);
});
this.$regenerateTotpButton.on("click", async () => { this.$regenerateTotpButton.on("click", async () => {
this.generateKey(); this.generateKey();
}); });
@ -54,7 +81,6 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
async generateKey() { async generateKey() {
server.get("totp/generate").then((result) => { server.get("totp/generate").then((result) => {
if (result.success) { if (result.success) {
// password changed so current protected session is invalid and needs to be cleared
this.$totpSecret.text(result.message); this.$totpSecret.text(result.message);
} else { } else {
toastService.showError(result.message); toastService.showError(result.message);
@ -62,13 +88,40 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
}); });
} }
optionsLoaded(options) { // async toggleTOTP() {
const isPasswordSet = options.isPasswordSet === "true"; // if (this.$totpEnabled)
// server.post("totp/enable").then((result) => {
// if (!result.success) toastService.showError(result.message);
// });
// else
// server.post("totp/disable").then((result) => {
// if (!result.success) toastService.showError(result.message);
// });
// }
optionsLoaded(options) {
// I need to make sure that this is actually pinging the server and that this information is being pulled
// because it is telling me "totpEnabled is not allowed to be changed" in a toast every time I check the box
server.get("totp/enabled").then((result) => {
if (result.success) {
console.log("Result message: " + typeof result.message);
console.log("Result message: " + result.message);
this.setCheckboxState(this.$totpEnabled, result.message);
console.log("TOTP Status: " + typeof result.message);
if (result.message) {
this.$totpSecretInput.prop("disabled", false);
this.$saveTotpButton.prop("disabled", false);
} else {
this.$totpSecretInput.prop("disabled", true);
this.$saveTotpButton.prop("disabled", true);
}
} else {
toastService.showError(result.message);
}
});
this.$widget.find(".old-password-form-group").toggle(isPasswordSet);
this.$savePasswordButton.text(
isPasswordSet ? "Change fff Password" : "Set Password"
);
this.$protectedSessionTimeout.val(options.protectedSessionTimeout); this.$protectedSessionTimeout.val(options.protectedSessionTimeout);
} }

View File

@ -3,55 +3,55 @@ import toastService from "../../../services/toast.js";
import NoteContextAwareWidget from "../../note_context_aware_widget.js"; import NoteContextAwareWidget from "../../note_context_aware_widget.js";
export default class OptionsWidget extends NoteContextAwareWidget { export default class OptionsWidget extends NoteContextAwareWidget {
constructor() { constructor() {
super(); super();
this.contentSized(); this.contentSized();
} }
async updateOption(name, value) { async updateOption(name, value) {
const opts = { [name]: value }; const opts = { [name]: value };
await this.updateMultipleOptions(opts); await this.updateMultipleOptions(opts);
} }
async updateMultipleOptions(opts) { async updateMultipleOptions(opts) {
await server.put('options', opts); await server.put("options", opts);
this.showUpdateNotification(); this.showUpdateNotification();
} }
showUpdateNotification() { showUpdateNotification() {
toastService.showPersistent({ toastService.showPersistent({
id: "options-change-saved", id: "options-change-saved",
title: "Options status", title: "Options status",
message: "Options change have been saved.", message: "Options change have been saved.",
icon: "slider", icon: "slider",
closeAfter: 2000 closeAfter: 2000,
}); });
} }
async updateCheckboxOption(name, $checkbox) { async updateCheckboxOption(name, $checkbox) {
const isChecked = $checkbox.prop("checked"); const isChecked = $checkbox.prop("checked");
return await this.updateOption(name, isChecked ? 'true' : 'false'); return await this.updateOption(name, isChecked ? "true" : "false");
} }
setCheckboxState($checkbox, optionValue) { setCheckboxState($checkbox, optionValue) {
$checkbox.prop('checked', optionValue === 'true'); $checkbox.prop("checked", optionValue === "true");
} }
optionsLoaded(options) {} optionsLoaded(options) {}
async refreshWithNote(note) { async refreshWithNote(note) {
const options = await server.get('options'); const options = await server.get("options");
this.optionsLoaded(options); this.optionsLoaded(options);
} }
async entitiesReloadedEvent({loadResults}) { async entitiesReloadedEvent({ loadResults }) {
if (loadResults.getOptionNames().length > 0) { if (loadResults.getOptionNames().length > 0) {
this.refresh(); this.refresh();
}
} }
}
} }

View File

@ -86,6 +86,7 @@ function updateOption(req: Request) {
function updateOptions(req: Request) { function updateOptions(req: Request) {
for (const optionName in req.body) { for (const optionName in req.body) {
console.log( optionName )
if (!update(optionName, req.body[optionName])) { if (!update(optionName, req.body[optionName])) {
// this should be improved // this should be improved
// it should return 400 instead of current 500, but at least it now rollbacks transaction // it should return 400 instead of current 500, but at least it now rollbacks transaction

View File

@ -1,3 +1,4 @@
import options = require("../../services/options");
const speakeasy = require("speakeasy"); const speakeasy = require("speakeasy");
function verifyOTPToken(guessedToken: any) { function verifyOTPToken(guessedToken: any) {
@ -18,7 +19,25 @@ function generateSecret() {
return { success: "true", message: speakeasy.generateSecret().base32 }; return { success: "true", message: speakeasy.generateSecret().base32 };
} }
function checkForTOTP() {
const totpEnabled = options.getOptionBool("totpEnabled")
return { success: "true", message: totpEnabled };
}
function enableTOTP() {
options.setOption("totpEnabled", true)
return { success: "true" };
}
function disableTOTP() {
options.setOption("totpEnabled", false)
return { success: "true" };
}
export = { export = {
verifyOTPToken, verifyOTPToken,
generateSecret, generateSecret,
checkForTOTP,
enableTOTP,
disableTOTP
}; };

View File

@ -14,6 +14,8 @@ import { AppRequest } from './route-interface';
function loginPage(req: Request, res: Response) { function loginPage(req: Request, res: Response) {
res.render('login', { res.render('login', {
failedAuth: false, failedAuth: false,
// totpEnabled: optionService.getOption("hasOTPEnabled"),
totpEnabled: false,
assetPath: assetPath, assetPath: assetPath,
appPath: appPath appPath: appPath
}); });
@ -59,6 +61,7 @@ function setPassword(req: Request, res: Response) {
function login(req: AppRequest, res: Response) { function login(req: AppRequest, res: Response) {
const guessedPassword = req.body.password; const guessedPassword = req.body.password;
const guessedTotp = req.body.token;
if (verifyPassword(guessedPassword)) { if (verifyPassword(guessedPassword)) {
const rememberMe = req.body.rememberMe; const rememberMe = req.body.rememberMe;

View File

@ -156,6 +156,9 @@ function register(app: express.Application) {
route(GET, "/setup", [], setupRoute.setupPage); route(GET, "/setup", [], setupRoute.setupPage);
apiRoute(GET, "/api/totp/generate", totp.generateSecret); apiRoute(GET, "/api/totp/generate", totp.generateSecret);
apiRoute(GET, "/api/totp/enabled", totp.checkForTOTP);
apiRoute(GET, "/api/totp/enable", totp.enableTOTP);
apiRoute(GET, "/api/totp/disable", totp.disableTOTP);
apiRoute(GET, "/api/tree", treeApiRoute.getTree); apiRoute(GET, "/api/tree", treeApiRoute.getTree);
apiRoute(PST, "/api/tree/load", treeApiRoute.load); apiRoute(PST, "/api/tree/load", treeApiRoute.load);

View File

@ -96,7 +96,8 @@ const defaultOptions = [
{ name: 'customSearchEngineName', value: 'DuckDuckGo', isSynced: true }, { name: 'customSearchEngineName', value: 'DuckDuckGo', isSynced: true },
{ name: 'customSearchEngineUrl', value: 'https://duckduckgo.com/?q={keyword}', isSynced: true }, { name: 'customSearchEngineUrl', value: 'https://duckduckgo.com/?q={keyword}', isSynced: true },
{ name: 'promotedAttributesOpenInRibbon', value: 'true', isSynced: true }, { name: 'promotedAttributesOpenInRibbon', value: 'true', isSynced: true },
{ name: 'editedNotesOpenInRibbon', value: 'true', isSynced: true } { name: 'editedNotesOpenInRibbon', value: 'true', isSynced: true },
// { name: 'totpEnabled', value: 'false', isSynced: false }
]; ];
function initStartupOptions() { function initStartupOptions() {

View File

@ -41,6 +41,21 @@
/> />
</div> </div>
</div> </div>
<% if( totpEnabled ) { %>
<div class="form-group">
<label for="otp-token">OTP Token</label>
<div class="controls">
<input
id="token"
name="token"
placeholder=""
class="form-control"
type="number"
maxlength="6"
required
/>
</div>
<% } %>
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<label> <label>