diff --git a/app/javascript/dashboard/App.vue b/app/javascript/dashboard/App.vue index a706e2df5..9c4d8b880 100644 --- a/app/javascript/dashboard/App.vue +++ b/app/javascript/dashboard/App.vue @@ -5,6 +5,7 @@ import NetworkNotification from './components/NetworkNotification.vue'; import UpdateBanner from './components/app/UpdateBanner.vue'; import PaymentPendingBanner from './components/app/PaymentPendingBanner.vue'; import PendingEmailVerificationBanner from './components/app/PendingEmailVerificationBanner.vue'; +import LowBackupCodesBanner from './components/app/LowBackupCodesBanner.vue'; import vueActionCable from './helper/actionCable'; import { useRouter } from 'vue-router'; import { useStore } from 'dashboard/composables/store'; @@ -30,6 +31,7 @@ export default { PaymentPendingBanner, WootSnackbarBox, PendingEmailVerificationBanner, + LowBackupCodesBanner, }, setup() { const router = useRouter(); @@ -140,6 +142,7 @@ export default { diff --git a/app/javascript/dashboard/components/app/LowBackupCodesBanner.vue b/app/javascript/dashboard/components/app/LowBackupCodesBanner.vue new file mode 100644 index 000000000..deb39d66f --- /dev/null +++ b/app/javascript/dashboard/components/app/LowBackupCodesBanner.vue @@ -0,0 +1,72 @@ + + + + diff --git a/app/javascript/dashboard/i18n/locale/en/mfa.json b/app/javascript/dashboard/i18n/locale/en/mfa.json index 8e356aad4..2f3c54b48 100644 --- a/app/javascript/dashboard/i18n/locale/en/mfa.json +++ b/app/javascript/dashboard/i18n/locale/en/mfa.json @@ -49,6 +49,11 @@ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account", "DISABLE_BUTTON": "Disable Two-Factor Authentication" }, + "LOW_BACKUP_CODES": { + "MESSAGE": "You have {count} backup codes remaining. Generate new codes to avoid getting locked out if you lose access to your authenticator.", + "NONE_LEFT": "You have no backup codes remaining. Generate new codes now to avoid getting locked out if you lose access to your authenticator.", + "ACTION": "Generate codes" + }, "DISABLE": { "TITLE": "Disable Two-Factor Authentication", "DESCRIPTION": "You'll need to enter your password and either a verification code from your authenticator app or a backup code to disable two-factor authentication.", diff --git a/app/services/mfa/management_service.rb b/app/services/mfa/management_service.rb index d4c01ec1f..187290710 100644 --- a/app/services/mfa/management_service.rb +++ b/app/services/mfa/management_service.rb @@ -78,6 +78,10 @@ class Mfa::ManagementService user.otp_backup_codes.present? end + def remaining_backup_codes_count + Array(user.otp_backup_codes).count { |code| code != 'XXXXXXXX' } + end + def mfa_enabled? user.otp_required_for_login? end diff --git a/app/views/api/v1/profile/mfa/show.json.jbuilder b/app/views/api/v1/profile/mfa/show.json.jbuilder index 4568f48f9..d9452aeec 100644 --- a/app/views/api/v1/profile/mfa/show.json.jbuilder +++ b/app/views/api/v1/profile/mfa/show.json.jbuilder @@ -1,3 +1,6 @@ json.feature_available Chatwoot.mfa_enabled? json.enabled @user.mfa_enabled? -json.backup_codes_generated @user.mfa_service.backup_codes_generated? if Chatwoot.mfa_enabled? +if Chatwoot.mfa_enabled? + json.backup_codes_generated @user.mfa_service.backup_codes_generated? + json.remaining_backup_codes @user.mfa_service.remaining_backup_codes_count +end