mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 01:15:03 +03:00
## 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 -->
51 lines
1.5 KiB
TypeScript
51 lines
1.5 KiB
TypeScript
import type {
|
|
AuthMFARecoveryCodesGenerateResponse,
|
|
MFARecoveryCodesGenerateParams,
|
|
} from '@supabase/auth-js'
|
|
import { useMutation } from '@tanstack/react-query'
|
|
import { toast } from 'sonner'
|
|
|
|
import { captureCriticalError } from '@/lib/error-reporting'
|
|
import { auth } from '@/lib/gotrue'
|
|
import { UseCustomMutationOptions } from '@/types'
|
|
|
|
export const recoveryCodesGenerate = async (params: MFARecoveryCodesGenerateParams) => {
|
|
const { error, data } = await auth.mfa.recoveryCodes.generate(params)
|
|
if (error) throw error
|
|
return data
|
|
}
|
|
|
|
type RecoveryCodesGenerateResponse = NonNullable<AuthMFARecoveryCodesGenerateResponse['data']>
|
|
type RecoveryCodesGenerateError = NonNullable<AuthMFARecoveryCodesGenerateResponse['error']>
|
|
|
|
export const useRecoveryCodesGenerateMutation = ({
|
|
onSuccess,
|
|
onError,
|
|
...options
|
|
}: Omit<
|
|
UseCustomMutationOptions<
|
|
RecoveryCodesGenerateResponse,
|
|
RecoveryCodesGenerateError,
|
|
MFARecoveryCodesGenerateParams
|
|
>,
|
|
'mutationFn'
|
|
> = {}) => {
|
|
return useMutation({
|
|
mutationFn: (vars) => {
|
|
return recoveryCodesGenerate(vars)
|
|
},
|
|
async onSuccess(data, variables, context) {
|
|
await onSuccess?.(data, variables, context)
|
|
},
|
|
async onError(data, variables, context) {
|
|
if (onError === undefined) {
|
|
toast.error(`Failed to get recovery codes: ${data.message}`)
|
|
} else {
|
|
onError(data, variables, context)
|
|
}
|
|
captureCriticalError(data, 'recovery codes status')
|
|
},
|
|
...options,
|
|
})
|
|
}
|