diff --git a/src/public/app/widgets/type_widgets/options/multi_factor_authentication.js b/src/public/app/widgets/type_widgets/options/multi_factor_authentication.js deleted file mode 100644 index f4f5ee365..000000000 --- a/src/public/app/widgets/type_widgets/options/multi_factor_authentication.js +++ /dev/null @@ -1,220 +0,0 @@ -import server from "../../../services/server.js"; -import toastService from "../../../services/toast.js"; -import OptionsWidget from "./options_widget.js"; - -const TPL = ` -
-

What is Multi-Factor Authentication?

- - 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 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 - break in. -
- -
-

OAuth/OpenID

- OpenID is a standardized way to let you log into websites using an account from another service, like Google, to verify your identity. -
- - - -
-
- User Account: -
- User Email: -
-
- -
-

Time-based One-Time Password

-
- - - -
-
- 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. -
-
- -
-

Generate TOTP Secret

- TOTP Secret Key -
- -
- -
-

Single Sign-on Recovery Keys

- Single sign-on recovery keys are used to login in the event you cannot access your Authenticator codes. Keep them somewhere safe and secure. -

- After a recovery key is used it cannot be used again. -

- - - - - - - - - - - - - - - - - - - -
-
-
-
-
-
- -
-`; - -export default class MultiFactorAuthenticationOptions extends OptionsWidget { - doRender() { - this.$widget = $(TPL); - - 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.$authenticatorCode = this.$widget.find(".authenticator-code"); - this.$generateRecoveryCodeButton = this.$widget.find( - ".generate-recovery-code" - ); - this.$oAuthEnabledCheckbox = this.$widget.find(".oauth-enabled-checkbox"); - this.$oauthLoginButton = this.$widget.find(".oauth-login-button"); - this.$UserAccountName = this.$widget.find(".user-account-name"); - this.$UserAccountEmail = this.$widget.find(".user-account-email"); - this.$envEnabledTOTP = this.$widget.find(".env-totp-enabled"); - this.$envEnabledOAuth = this.$widget.find(".env-oauth-enabled"); - - - this.$recoveryKeys = []; - - for (let i = 0; i < 8; i++) - { - this.$recoveryKeys.push(this.$widget.find(".key_" + i)); - } - - this.$generateRecoveryCodeButton.on("click", async () => { - this.setRecoveryKeys(); - }); - - this.$regenerateTotpButton.on("click", async () => { - this.generateKey(); - }); - - this.$protectedSessionTimeout = this.$widget.find( - ".protected-session-timeout-in-seconds" - ); - this.$protectedSessionTimeout.on("change", () => - this.updateOption( - "protectedSessionTimeout", - this.$protectedSessionTimeout.val() - ) - ); - - this.displayRecoveryKeys(); - } - - async setRecoveryKeys() { - server.get("totp_recovery/generate").then((result) => { - if (!result.success) { - toastService.showError("Error in revevery code generation!"); - return; - } - this.keyFiller(result.recoveryCodes); - server.post("totp_recovery/set", { - recoveryCodes: result.recoveryCodes, - }); - }); - } - - async keyFiller(values) { - // Forces values to be a string so it doesn't error out when I split. - // Will be a non-issue when I update everything to typescript. - const keys = (values + "").split(","); - for (let i = 0; i < keys.length; i++) this.$recoveryKeys[i].text(keys[i]); - } - - async generateKey() { - server.get("totp/generate").then((result) => { - if (result.success) { - this.$totpSecret.text(result.message); - } else { - toastService.showError(result.message); - } - }); - } - - optionsLoaded(options) { - server.get("oauth/status").then((result) => { - if (result.enabled) { - this.$oAuthEnabledCheckbox.prop("checked", result.enabled); - this.$UserAccountName.text(result.name); - this.$UserAccountEmail.text(result.email); - }else - this.$envEnabledOAuth.text( - "set SSO_ENABLED as environment variable to 'true' to enable (Requires restart)" - ); - }); - - server.get("totp/status").then((result) => { - if (result.enabled){ - this.$totpEnabled.prop("checked", result.message); - this.$authenticatorCode.prop("disabled", !result.message); - this.$generateRecoveryCodeButton.prop("disabled", !result.message); - } - else { - this.$totpEnabled.prop("checked", false); - this.$totpEnabled.prop("disabled", true); - this.$authenticatorCode.prop("disabled", true); - this.$generateRecoveryCodeButton.prop("disabled", true); - - this.$envEnabledTOTP.text( - "Set TOTP_ENABLED as environment variable to 'true' to enable (Requires restart)" - ); - } - }); - this.$protectedSessionTimeout.val(options.protectedSessionTimeout); - } - - displayRecoveryKeys() { - server.get("totp_recovery/enabled").then((result) => { - if (!result.success) { - this.keyFiller(Array(8).fill("Error generating recovery keys!")); - return; - } - - if (!result.keysExist) { - this.keyFiller(Array(8).fill("No key set")); - this.$generateRecoveryCodeButton.text("Generate Recovery Codes"); - return; - } - }); - server.get("totp_recovery/used").then((result) => { - this.keyFiller((result.usedRecoveryCodes + "").split(",")); - this.$generateRecoveryCodeButton.text("Regenerate Recovery Codes"); - }); - } -} 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 new file mode 100644 index 000000000..7df18c301 --- /dev/null +++ b/src/public/app/widgets/type_widgets/options/multi_factor_authentication.ts @@ -0,0 +1,245 @@ +import server from "../../../services/server.js"; +import toastService from "../../../services/toast.js"; +import OptionsWidget from "./options_widget.js"; +import type { OptionMap } from "../../../../../services/options_interface.js"; + +const TPL = ` +
+

What is Multi-Factor Authentication?

+ + 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/OpenID

+ OpenID is a standardized way to let you log into websites using an account from another service, like Google, to verify your identity. +
+ + + +
+
+ User Account: +
+ User Email: +
+
+ +
+

Time-based One-Time Password

+
+ + + +
+
+ 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. +
+
+ +
+

Generate TOTP Secret

+ TOTP Secret Key +
+ +
+ +
+

Single Sign-on Recovery Keys

+ Single sign-on recovery keys are used to login in the event you cannot access your Authenticator codes. Keep them somewhere safe and secure. +

+ After a recovery key is used it cannot be used again. +

+ + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+ +
+`; + +interface OAuthStatus { + enabled: boolean; + name?: string; + email?: string; +} + +interface TOTPStatus { + enabled: boolean; + message: boolean; +} + +interface RecoveryKeysResponse { + success: boolean; + recoveryCodes?: string[]; + keysExist?: boolean; + usedRecoveryCodes?: string[]; +} + +export default class MultiFactorAuthenticationOptions extends OptionsWidget { + private $regenerateTotpButton!: JQuery; + private $totpEnabled!: JQuery; + private $totpSecret!: JQuery; + private $totpSecretInput!: JQuery; + private $authenticatorCode!: JQuery; + private $generateRecoveryCodeButton!: JQuery; + private $oAuthEnabledCheckbox!: JQuery; + private $oauthLoginButton!: JQuery; + private $UserAccountName!: JQuery; + private $UserAccountEmail!: JQuery; + private $envEnabledTOTP!: JQuery; + private $envEnabledOAuth!: JQuery; + private $recoveryKeys: JQuery[] = []; + private $protectedSessionTimeout!: JQuery; + + doRender() { + this.$widget = $(TPL); + + 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.$authenticatorCode = this.$widget.find(".authenticator-code"); + this.$generateRecoveryCodeButton = this.$widget.find(".generate-recovery-code"); + this.$oAuthEnabledCheckbox = this.$widget.find(".oauth-enabled-checkbox"); + this.$oauthLoginButton = this.$widget.find(".oauth-login-button"); + this.$UserAccountName = this.$widget.find(".user-account-name"); + this.$UserAccountEmail = this.$widget.find(".user-account-email"); + this.$envEnabledTOTP = this.$widget.find(".env-totp-enabled"); + this.$envEnabledOAuth = this.$widget.find(".env-oauth-enabled"); + + this.$recoveryKeys = []; + for (let i = 0; i < 8; i++) { + this.$recoveryKeys.push(this.$widget.find(".key_" + i)); + } + + this.$generateRecoveryCodeButton.on("click", async () => { + await this.setRecoveryKeys(); + }); + + this.$regenerateTotpButton.on("click", async () => { + await this.generateKey(); + }); + + this.$protectedSessionTimeout = this.$widget.find(".protected-session-timeout-in-seconds"); + this.$protectedSessionTimeout.on("change", () => { + this.updateOption("protectedSessionTimeout", this.$protectedSessionTimeout.val()); + }); + + this.displayRecoveryKeys(); + } + + async setRecoveryKeys() { + const result = await server.get("totp_recovery/generate"); + if (!result.success) { + toastService.showError("Error in recovery code generation!"); + return; + } + if (result.recoveryCodes) { + this.keyFiller(result.recoveryCodes); + await server.post("totp_recovery/set", { + recoveryCodes: result.recoveryCodes, + }); + } + } + + private keyFiller(values: string[]) { + const keys = values.join(",").split(","); + for (let i = 0; i < keys.length; i++) { + this.$recoveryKeys[i].text(keys[i]); + } + } + + async generateKey() { + const result = await server.get<{ success: boolean; message: string }>("totp/generate"); + if (result.success) { + this.$totpSecret.text(result.message); + } else { + toastService.showError(result.message); + } + } + + optionsLoaded(options: OptionMap) { + server.get("oauth/status").then((result) => { + if (result.enabled) { + this.$oAuthEnabledCheckbox.prop("checked", result.enabled); + if (result.name) this.$UserAccountName.text(result.name); + if (result.email) this.$UserAccountEmail.text(result.email); + } else { + this.$envEnabledOAuth.text( + "set SSO_ENABLED as environment variable to 'true' to enable (Requires restart)" + ); + } + }); + + server.get("totp/status").then((result) => { + if (result.enabled) { + this.$totpEnabled.prop("checked", result.message); + this.$authenticatorCode.prop("disabled", !result.message); + this.$generateRecoveryCodeButton.prop("disabled", !result.message); + } else { + this.$totpEnabled.prop("checked", false); + this.$totpEnabled.prop("disabled", true); + this.$authenticatorCode.prop("disabled", true); + this.$generateRecoveryCodeButton.prop("disabled", true); + + this.$envEnabledTOTP.text( + "Set TOTP_ENABLED as environment variable to 'true' to enable (Requires restart)" + ); + } + }); + this.$protectedSessionTimeout.val(Number(options.protectedSessionTimeout)); + } + + async displayRecoveryKeys() { + const result = await server.get("totp_recovery/enabled"); + if (!result.success) { + this.keyFiller(Array(8).fill("Error generating recovery keys!")); + return; + } + + if (!result.keysExist) { + this.keyFiller(Array(8).fill("No key set")); + this.$generateRecoveryCodeButton.text("Generate Recovery Codes"); + return; + } + + const usedResult = await server.get("totp_recovery/used"); + if (usedResult.usedRecoveryCodes) { + this.keyFiller(usedResult.usedRecoveryCodes); + this.$generateRecoveryCodeButton.text("Regenerate Recovery Codes"); + } + } +}