Saving TOTP secret requires password verification

This commit is contained in:
Chesspro13 2024-05-06 12:22:25 -07:00
parent 18a2305c35
commit f466cd0b5a
2 changed files with 26 additions and 18 deletions

View File

@ -2,19 +2,13 @@ import server from "../../../services/server.js";
import protectedSessionHolder from "../../../services/protected_session_holder.js"; import protectedSessionHolder from "../../../services/protected_session_holder.js";
import toastService from "../../../services/toast.js"; import toastService from "../../../services/toast.js";
import OptionsWidget from "./options_widget.js"; import OptionsWidget from "./options_widget.js";
// import { randomBytes } from "crypto";
// import { generateSecret } from "../../../services/totp.js";
// const speakeasy = require("speakeasy");
// ${speakeasy.generateSecret().base32}
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">
<h4 class="mfa-heading"></h4> <h4 class="mfa-heading"></h4>
<div class="alert alert-warning" role="alert" style="font-weight: bold; color: red !important;"> <div class="alert alert-warning" role="alert" style="font-weight: bold; color: red !important;">
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 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> <br>
@ -27,14 +21,19 @@ const TPL = `
</div> </div>
<br> <br>
<div class="options-section"> <div>
<label> <div class="form-group">
TOTP Secret <label>Password confirmation</label>
</label> <input class="password form-control" type="password">
<input class="totp-secret-input form-control" disabled="true" type="text"> </div>
<button class="save-totp" disabled="true"> Save TOTP Secret </button> <div class="options-section">
</div> <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>
</div>
<br> <br>
<h4> Generate TOTP Secret </h4> <h4> Generate TOTP Secret </h4>
<span class="totp-secret" > </span> <span class="totp-secret" > </span>
@ -52,8 +51,9 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
this.$totpSecret = this.$widget.find(".totp-secret"); this.$totpSecret = this.$widget.find(".totp-secret");
this.$totpSecretInput = this.$widget.find(".totp-secret-input"); this.$totpSecretInput = this.$widget.find(".totp-secret-input");
this.$saveTotpButton = this.$widget.find(".save-totp"); this.$saveTotpButton = this.$widget.find(".save-totp");
this.$password = this.$widget.find(".password");
this.$mfaHeadding.text("Multi-Factor Authentication"); this.$mfaHeadding.text("Time-Based One Time Password (TOTP)");
this.generateKey(); this.generateKey();
this.$totpEnabled.on("change", async () => { this.$totpEnabled.on("change", async () => {
@ -65,7 +65,7 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
}); });
this.$saveTotpButton.on("click", async () => { this.$saveTotpButton.on("click", async () => {
this.save(); this.saveTotpSecret();
}); });
this.$protectedSessionTimeout = this.$widget.find( this.$protectedSessionTimeout = this.$widget.find(
@ -102,6 +102,7 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
this.$saveTotpButton.prop("disabled", !result.message); this.$saveTotpButton.prop("disabled", !result.message);
this.$totpSecret.prop("disapbled", !result.message); this.$totpSecret.prop("disapbled", !result.message);
this.$regenerateTotpButton.prop("disabled", !result.message); this.$regenerateTotpButton.prop("disabled", !result.message);
this.$password.prop("disabled", !result.message);
} else { } else {
toastService.showError(result.message); toastService.showError(result.message);
} }
@ -110,7 +111,7 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
this.$protectedSessionTimeout.val(options.protectedSessionTimeout); this.$protectedSessionTimeout.val(options.protectedSessionTimeout);
} }
save() { saveTotpSecret() {
const key = this.$totpSecretInput.val(); const key = this.$totpSecretInput.val();
const regex = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/; const regex = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/;
@ -126,6 +127,7 @@ export default class MultiFactorAuthenticationOptions extends OptionsWidget {
server server
.post("totp/set", { .post("totp/set", {
secret: this.$totpSecretInput.val(), secret: this.$totpSecretInput.val(),
password: this.$password.val(),
}) })
.then((result) => { .then((result) => {
if (result.success) { if (result.success) {

View File

@ -1,7 +1,9 @@
import options = require("../../services/options"); import options = require("../../services/options");
import totp_secret = require("../../services/encryption/totp_secret"); import totp_secret = require("../../services/encryption/totp_secret");
import passwordEncryptionService = require('../../services/encryption/password_encryption');
import { Request } from "express"; import { Request } from "express";
import totp_fs = require("../../services/totp_secret"); import totp_fs = require("../../services/totp_secret");
import ValidationError = require('../../errors/validation_error');
const speakeasy = require("speakeasy"); const speakeasy = require("speakeasy");
function verifyOTPToken(guessedToken: any) { function verifyOTPToken(guessedToken: any) {
@ -36,6 +38,10 @@ function disableTOTP() {
} }
function setTotpSecret(req: Request) { function setTotpSecret(req: Request) {
if(!passwordEncryptionService.verifyPassword(req.body.password))
throw new ValidationError("Incorrect password reset confirmation");
const regex = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/; const regex = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/;
if (req.body.secret.length != 52) return; if (req.body.secret.length != 52) return;
if (regex.test(req.body.secret)) return; if (regex.test(req.body.secret)) return;