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.
</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>
<br>
<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.$regenerateTotpButton = this.$widget.find(".regenerate-totp");
this.$totpEnabled = this.$widget.find(".totp-enabled");
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.generateKey();
@ -36,6 +59,10 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
// var gen = require("speakeasy");
// toastService.showMessage("***REMOVED***");
this.$totpEnabled.on("change", async () => {
this.updateCheckboxOption("totpEnabled", this.$totpEnabled);
});
this.$regenerateTotpButton.on("click", async () => {
this.generateKey();
});
@ -54,7 +81,6 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
async generateKey() {
server.get("totp/generate").then((result) => {
if (result.success) {
// password changed so current protected session is invalid and needs to be cleared
this.$totpSecret.text(result.message);
} else {
toastService.showError(result.message);
@ -62,13 +88,40 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
});
}
optionsLoaded(options) {
const isPasswordSet = options.isPasswordSet === "true";
// async toggleTOTP() {
// 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);
}

View File

@ -16,7 +16,7 @@ export default class OptionsWidget extends NoteContextAwareWidget {
}
async updateMultipleOptions(opts) {
await server.put('options', opts);
await server.put("options", opts);
this.showUpdateNotification();
}
@ -27,29 +27,29 @@ export default class OptionsWidget extends NoteContextAwareWidget {
title: "Options status",
message: "Options change have been saved.",
icon: "slider",
closeAfter: 2000
closeAfter: 2000,
});
}
async updateCheckboxOption(name, $checkbox) {
const isChecked = $checkbox.prop("checked");
return await this.updateOption(name, isChecked ? 'true' : 'false');
return await this.updateOption(name, isChecked ? "true" : "false");
}
setCheckboxState($checkbox, optionValue) {
$checkbox.prop('checked', optionValue === 'true');
$checkbox.prop("checked", optionValue === "true");
}
optionsLoaded(options) {}
async refreshWithNote(note) {
const options = await server.get('options');
const options = await server.get("options");
this.optionsLoaded(options);
}
async entitiesReloadedEvent({loadResults}) {
async entitiesReloadedEvent({ loadResults }) {
if (loadResults.getOptionNames().length > 0) {
this.refresh();
}

View File

@ -86,6 +86,7 @@ function updateOption(req: Request) {
function updateOptions(req: Request) {
for (const optionName in req.body) {
console.log( optionName )
if (!update(optionName, req.body[optionName])) {
// this should be improved
// 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");
function verifyOTPToken(guessedToken: any) {
@ -18,7 +19,25 @@ function generateSecret() {
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 = {
verifyOTPToken,
generateSecret,
checkForTOTP,
enableTOTP,
disableTOTP
};

View File

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

View File

@ -156,6 +156,9 @@ function register(app: express.Application) {
route(GET, "/setup", [], setupRoute.setupPage);
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(PST, "/api/tree/load", treeApiRoute.load);

View File

@ -96,7 +96,8 @@ const defaultOptions = [
{ name: 'customSearchEngineName', value: 'DuckDuckGo', isSynced: true },
{ name: 'customSearchEngineUrl', value: 'https://duckduckgo.com/?q={keyword}', 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() {

View File

@ -41,6 +41,21 @@
/>
</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="checkbox">
<label>