diff --git a/app/frontend/static/assets/css/dark/style.css b/app/frontend/static/assets/css/dark/style.css index cae93650..5e781726 100755 --- a/app/frontend/static/assets/css/dark/style.css +++ b/app/frontend/static/assets/css/dark/style.css @@ -7899,7 +7899,7 @@ a.badge-dark.focus { display: block; padding: 0.75rem 1.25rem; margin-bottom: -1px; - background-color: var(--select-bg); + background-color: var(--outline); border: 1px solid rgba(0, 0, 0, 0.125); } diff --git a/app/frontend/static/assets/css/mfa.css b/app/frontend/static/assets/css/mfa.css new file mode 100644 index 00000000..d818b69b --- /dev/null +++ b/app/frontend/static/assets/css/mfa.css @@ -0,0 +1,30 @@ +.bootbox-custom-layout { + display: flex; + justify-content: space-between; + gap: 20px; +} + +.bootbox-custom-layout .left-panel { + flex: 1; + text-align: center; +} + +.bootbox-custom-layout .right-panel { + flex: 1; +} + +.bootbox-custom-layout .qr-code { + width: 100%; + max-width: 200px; + /* Adjust as needed */ + margin: auto; +} + +.bootbox-custom-layout .list-group { + margin-top: 10px; +} + +.bootbox .modal-dialog { + max-width: 900px; + /* Wider modal */ +} \ No newline at end of file diff --git a/app/frontend/templates/panel/panel_edit_user_otp.html b/app/frontend/templates/panel/panel_edit_user_otp.html index 7a762059..2bd616d0 100644 --- a/app/frontend/templates/panel/panel_edit_user_otp.html +++ b/app/frontend/templates/panel/panel_edit_user_otp.html @@ -6,6 +6,7 @@ {% block title %}Crafty Controller - {{ translate('userConfig', 'editTOTP', data['lang']) }}{% end %} {% block content %} +
@@ -54,7 +55,9 @@

 {{ translate('otp', 'otpTitle', data['lang']) }}

-
@@ -129,17 +132,37 @@ // Function to create the modal response content function createResponseDiv(data) { - let backupcodes = ``; + let backupcodes = `` + if (data["backup_codes"].length == 0) { + backupcodes = `
+ ${$("#createButton").data("backup-codes-max")} +
` + } + const contentHTML = ` -
-
- ${data["otp"]["totp_secret"]} -
-
-
    ${backupcodes}
+
+
+
+
    +
  • ${data["otp"]["totp_secret"]}
  • +
      + + +
      + + +
      +
+
+

${$("#createButton").data("backup-codes")}

+
    + ${backupcodes} +
+
`; let responseDIV = document.createElement("div"); @@ -173,17 +196,20 @@ let resdiv = createResponseDiv(responseData.data); bootbox.dialog({ title: 'Your TOTP QR Code', + size: "large", + closeButton: false, message: resdiv, // Display the generated QR code buttons: { close: { label: 'Close', - className: 'btn-secondary', + className: 'bb-close-button', callback: function () { // Optional: Any cleanup logic } } } }); + $(`.bb-close-button`).prop("disabled", true); // Generate the QR Code with the TOTP secret, user name, and issuer generateTotpQrCode(responseData.data["otp"]["totp_secret"], responseData.data["otp"]["user"]["username"], "CraftyController"); diff --git a/app/frontend/templates/public/login.html b/app/frontend/templates/public/login.html index 66ff4c69..b470f858 100644 --- a/app/frontend/templates/public/login.html +++ b/app/frontend/templates/public/login.html @@ -112,7 +112,7 @@
@@ -206,10 +206,11 @@ "password": formDataObject.password, } if (formDataObject.totp != "") { + let key = $("#2fa-type").val(); body = { "username": formDataObject.username, "password": formDataObject.password, - "totp": formDataObject.totp, + [key]: formDataObject.totp, } } let res = await fetch(`/api/v2/auth/login/`, { diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 44cb970c..119b5a88 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -263,6 +263,8 @@ "no": "No", "2faCreate": "Add New 2FA Method", "backupCodes": "New 2FA method created. Please copy your backup codes.", + "backupCodesMax": "A maximum of 6 back codes has already been reached.", + "saveWarn": "Make sure to save these backup codes or you could lose access", "confirm2FA": "Please Confirm Authenticator Code", "otpTitle": "2FA Methods", "newOTP": "Add New 2 Factor Auth" @@ -698,6 +700,7 @@ "otpReq": "TOTP is required for super users. You cannot remove your only TOTP method." }, "validators": { + "2FAerror": "2 Factor code must be 6 digits (i.e. 000000) or a backup code of 16 characters separated every 4 by - (i.e. ABCD-EFGH-IJKL-MNOP)", "backupName": "Backup name must be a string and a minimum length of 3.", "enumErr": "failed validating. Acceptable data includes: ", "filesPageLen": "length must be greater than 1 for property",