diff --git a/apps/studio/components/interfaces/Organization/SecuritySettings.tsx b/apps/studio/components/interfaces/Organization/SecuritySettings.tsx
index a8ea11d9644..cba6de2cbbc 100644
--- a/apps/studio/components/interfaces/Organization/SecuritySettings.tsx
+++ b/apps/studio/components/interfaces/Organization/SecuritySettings.tsx
@@ -1,29 +1,18 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
+import Link from 'next/link'
import { useEffect } from 'react'
import { useForm } from 'react-hook-form'
import { toast } from 'sonner'
-import {
- Button,
- Card,
- CardContent,
- CardFooter,
- Form,
- FormControl,
- FormField,
- Switch,
- Tooltip,
- TooltipContent,
- TooltipTrigger,
-} from 'ui'
+import { Button, Card, CardContent, CardFooter, Form, FormControl, FormField, Switch } from 'ui'
+import { Admonition } from 'ui-patterns'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
import { z } from 'zod'
import { ScaffoldContainer, ScaffoldSection } from '@/components/layouts/Scaffold'
import AlertError from '@/components/ui/AlertError'
-import { InlineLink } from '@/components/ui/InlineLink'
import NoPermission from '@/components/ui/NoPermission'
import { UpgradeToPro } from '@/components/ui/UpgradeToPro'
import { useOrganizationMembersQuery } from '@/data/organizations/organization-members-query'
@@ -41,7 +30,13 @@ const schema = z.object({
export const SecuritySettings = () => {
const { slug } = useParams()
const { profile } = useProfile()
- const { data: members } = useOrganizationMembersQuery({ slug })
+ const {
+ data: members,
+ error: membersError,
+ isPending: isLoadingMembers,
+ isError: isMembersError,
+ isSuccess: isSuccessMembers,
+ } = useOrganizationMembersQuery({ slug })
const { can: canReadMfaConfig, isLoading: isLoadingPermissions } = useAsyncCheckPermissions(
PermissionAction.READ,
@@ -89,7 +84,19 @@ export const SecuritySettings = () => {
}, [mfaConfig, form])
const hasMFAEnabled =
- members?.find((member) => member.primary_email == profile?.primary_email)?.mfa_enabled || false
+ members?.find((member) => member.primary_email == profile?.primary_email)?.mfa_enabled ?? false
+
+ const requiresPersonalMfa = isSuccessMembers && canUpdateMfaConfig && !hasMFAEnabled
+
+ const isLoadingMfaEnforcementSettings =
+ isLoadingMfa || isLoadingPermissions || isLoadingEntitlement || isLoadingMembers
+ const hasMfaConfigError = (isErrorMfa || Boolean(mfaError)) && hasAccessToEnforceMfa
+ const canShowMfaEnforcementForm =
+ isSuccessMfa && hasAccessToEnforceMfa && isSuccessMembers && !requiresPersonalMfa
+ const isMfaEnforcementSwitchDisabled =
+ !hasAccessToEnforceMfa || !canUpdateMfaConfig || isUpdatingMfa
+ const isSaveMfaEnforcementDisabled =
+ isMfaEnforcementSwitchDisabled || isLoadingMfa || !form.formState.isDirty
const onSubmit = (values: { enforceMfa: boolean }) => {
if (!slug || !hasAccessToEnforceMfa) return
@@ -108,7 +115,7 @@ export const SecuritySettings = () => {
/>
) : (
<>
- {isLoadingMfa || isLoadingPermissions || isLoadingEntitlement ? (
+ {isLoadingMfaEnforcementSettings ? (
@@ -116,13 +123,31 @@ export const SecuritySettings = () => {
) : !canReadMfaConfig ? (
- ) : null}
+ ) : (
+ requiresPersonalMfa && (
+
+ Set up MFA
+
+ }
+ />
+ )
+ )}
- {(isErrorMfa || mfaError) && hasAccessToEnforceMfa && (
+ {isMembersError && (
+
+ )}
+
+ {hasMfaConfigError && (
)}
- {isSuccessMfa && hasAccessToEnforceMfa && (
+ {canShowMfaEnforcementForm && (