Files
supabase/apps/studio/data/oauth-custom-providers/oauth-custom-providers-query.ts
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

66 lines
2.6 KiB
TypeScript

import { AuthError, type CustomOAuthProvider } from '@supabase/auth-js'
import { useQuery } from '@tanstack/react-query'
import { oAuthCustomProvidersKeys } from './keys'
import { useAuthConfigQuery } from '@/data/auth/auth-config-query'
import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
import { handleError } from '@/data/fetchers'
import { createProjectSupabaseClient } from '@/lib/project-supabase-client'
import type { ResponseError, UseCustomQueryOptions } from '@/types'
export type OAuthCustomProvidersVariables = {
projectRef: string | undefined
page?: number
}
export async function getOAuthCustomProviders({
projectRef,
clientEndpoint,
}: OAuthCustomProvidersVariables & { clientEndpoint: string | undefined }) {
if (!projectRef) throw new Error('Project reference is required')
if (!clientEndpoint) throw new Error('Client endpoint is required')
const supabaseClient = await createProjectSupabaseClient(projectRef, clientEndpoint)
const { data, error } = await supabaseClient.auth.admin.customProviders.listProviders()
if (error) {
// Non-JSON responses from the API indicate custom providers aren't enabled.
// Different browsers/SDK versions produce different JSON parse error messages,
// so we check broadly for JSON parse indicators.
if (/JSON\.parse|Unexpected token|unexpected.*character/i.test(error.message)) {
handleError(new AuthError('Custom providers are not enabled for this project'))
} else {
handleError(error)
}
}
return data.providers as CustomOAuthProvider[]
}
export type OAuthCustomProvidersData = Awaited<ReturnType<typeof getOAuthCustomProviders>>
export type OAuthCustomProvidersError = ResponseError
export const useOAuthCustomProvidersQuery = <TData = OAuthCustomProvidersData>(
{ projectRef }: OAuthCustomProvidersVariables,
{
enabled = true,
...options
}: UseCustomQueryOptions<OAuthCustomProvidersData, OAuthCustomProvidersError, TData> = {}
) => {
const { hostEndpoint: clientEndpoint } = useProjectApiUrl({ projectRef })
const { data: authConfig, isSuccess: isSuccessConfig } = useAuthConfigQuery({ projectRef })
const isOAuthCustomProvidersEnabled = !!authConfig?.CUSTOM_OAUTH_ENABLED
return useQuery<OAuthCustomProvidersData, OAuthCustomProvidersError, TData>({
queryKey: oAuthCustomProvidersKeys.list(projectRef, clientEndpoint),
queryFn: () => getOAuthCustomProviders({ projectRef, clientEndpoint }),
enabled:
enabled &&
typeof projectRef !== 'undefined' &&
!!clientEndpoint &&
isSuccessConfig &&
isOAuthCustomProvidersEnabled,
...options,
})
}