Files
supabase/apps/studio/components/interfaces/Account/TOTPFactors/UnenrollRecoveryCodesModal.tsx
T
Gildas Garcia 63bedef77f MFA Recovery codes: allow users to download their recovery codes (#50267)
## What kind of change does this PR introduce?

After users have set up a new MFA (first or not), we must:

- check whether recovery codes have already been generated
- if there are none, generate recovery codes and display them, "forcing"
users to copy them
- if already generated, show them how many are still available

> [!NOTE]
> The _Delete my recovery codes_ button in last screenshot only appear
on local and staging environments

## How to test

- On an account that doesn't have recovery codes generated yet and has
an MFA added
- You should see an admonition suggesting to generate the codes

## Screenshots

<img width="729" height="306" alt="image"
src="https://github.com/user-attachments/assets/79ba3870-4ef8-4571-9fd6-36eed20c9c24"
/>

<img width="550" height="356" alt="image"
src="https://github.com/user-attachments/assets/1632611a-996a-470d-b6cd-a4693b0f4602"
/>

<img width="719" height="205" alt="image"
src="https://github.com/user-attachments/assets/73cef611-05cf-4fac-bbd2-243f9b28e48d"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added support for generating, copying, and confirming MFA recovery
codes.
- Added recovery-code status visibility, including remaining and
exhausted codes.
  - Added the ability to delete recovery codes with confirmation.
- Added clear loading, success, and error states for recovery-code
actions.
  - Recovery-code status refreshes after codes are generated or deleted.

- **Bug Fixes**
- Recovery-code notices now remain visible when all codes have been
used.
  - Recovery-code dialogs can now be closed after generation errors.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-15 11:16:47 +02:00

37 lines
1.2 KiB
TypeScript

import { useState } from 'react'
import { Button } from 'ui'
import { TextConfirmModal } from '@/components/ui/TextConfirmModalWrapper'
import { useRecoveryCodesUnenrollMutation } from '@/data/recovery-codes/recovery-codes-unenroll'
export const UnenrollRecoveryCodesModal = () => {
const [showConfirm, setShowConfirm] = useState(false)
const recoveryCodesGenerateMutation = useRecoveryCodesUnenrollMutation({
onSuccess: () => {
setShowConfirm(false)
},
})
return (
<>
<Button onClick={() => setShowConfirm(true)}>Delete my recovery codes</Button>
<TextConfirmModal
visible={showConfirm}
size="small"
variant="destructive"
title="Delete my recovery codes"
confirmPlaceholder="DELETE"
confirmString="DELETE"
confirmLabel="Delete"
loading={recoveryCodesGenerateMutation.isPending}
onConfirm={() => recoveryCodesGenerateMutation.mutate()}
onCancel={() => setShowConfirm(false)}
>
<p className="text-sm">
Your recovery codes won't work anymore. Make sure to regenerate them if needed.
</p>
</TextConfirmModal>
</>
)
}