// SPDX-License-Identifier: AGPL-3.0-or-later import * as Modal from '@app/features/app/components/dialogs/Modal'; import type {BackupCodesChallenge} from '@app/features/auth/commands/MfaCommands'; import * as MfaCommands from '@app/features/auth/commands/MfaCommands'; import {BackupCodesModal} from '@app/features/auth/components/modals/BackupCodesModal'; import {Button} from '@app/features/ui/button/Button'; import * as ModalCommands from '@app/features/ui/commands/ModalCommands'; import {modal} from '@app/features/ui/commands/ModalCommands'; import * as ToastCommands from '@app/features/ui/commands/ToastCommands'; import type {User} from '@app/features/user/models/User'; import * as FormUtils from '@app/lib/forms'; import type {BackupCode} from '@fluxer/schema/src/domains/user/UserResponseSchemas'; import {msg} from '@lingui/core/macro'; import {Trans, useLingui} from '@lingui/react/macro'; import {observer} from 'mobx-react-lite'; import {useState} from 'react'; const BACKUP_CODES_REGENERATED_DESCRIPTOR = msg({ message: 'Backup codes regenerated', comment: 'Toast shown after new account recovery backup codes were generated.', }); const REGENERATE_BACKUP_CODES_DESCRIPTOR = msg({ message: 'Regenerate backup codes', comment: 'Security confirmation title for replacing account recovery backup codes.', }); interface BackupCodesRegenerateModalProps { user: User; challenge?: BackupCodesChallenge; } interface RegenerateResult { backupCodes: Array; challenge?: BackupCodesChallenge; } async function regenerateBackupCodes(challenge?: BackupCodesChallenge): Promise { if (challenge) { try { return {backupCodes: await MfaCommands.regenerateBackupCodesWithChallenge(challenge), challenge}; } catch (error) { if (!MfaCommands.isExpiredBackupCodesChallengeError(error)) { throw error; } } } return {backupCodes: await MfaCommands.getBackupCodes(true)}; } export const BackupCodesRegenerateModal = observer(({user, challenge}: BackupCodesRegenerateModalProps) => { const {i18n} = useLingui(); const [isSubmitting, setIsSubmitting] = useState(false); const handleConfirm = async () => { setIsSubmitting(true); try { const result = await regenerateBackupCodes(challenge); ModalCommands.pop(); ModalCommands.update('backup-codes', () => modal(() => ( )), ); ToastCommands.createToast({ type: 'success', children: i18n._(BACKUP_CODES_REGENERATED_DESCRIPTOR), }); } catch (error) { FormUtils.pushApiErrorModal(i18n, error); } finally { setIsSubmitting(false); } }; return ( This will invalidate your existing backup codes and generate new ones. ); });