+
{ongoingIncident && }
{showNoticeBanner && }
{profile !== undefined && }
diff --git a/apps/studio/components/interfaces/App/MonacoThemeProvider.tsx b/apps/studio/components/interfaces/App/MonacoThemeProvider.tsx
new file mode 100644
index 00000000000..92b9fb6e0f8
--- /dev/null
+++ b/apps/studio/components/interfaces/App/MonacoThemeProvider.tsx
@@ -0,0 +1,44 @@
+import { useMonaco } from '@monaco-editor/react'
+import { useTheme } from 'next-themes'
+import { useMemo } from 'react'
+
+const getTheme = (theme: string) => {
+ const isDarkMode = theme.includes('dark')
+ // [TODO] Probably need better theming for light mode
+ return {
+ base: isDarkMode ? ('vs-dark' as const) : ('vs' as const), // can also be vs-dark or hc-black
+ inherit: true, // can also be false to completely replace the builtin rules
+ rules: [
+ { token: '', background: isDarkMode ? '1f1f1f' : 'f0f0f0' },
+ {
+ token: '',
+ background: isDarkMode ? '1f1f1f' : 'f0f0f0',
+ foreground: isDarkMode ? 'd4d4d4' : '444444',
+ },
+ { token: 'string.sql', foreground: '24b47e' },
+ { token: 'comment', foreground: '666666' },
+ { token: 'predefined.sql', foreground: isDarkMode ? 'D4D4D4' : '444444' },
+ ],
+ colors: { 'editor.background': isDarkMode ? '#1f1f1f' : '#f0f0f0' },
+ }
+}
+
+/**
+ * This component is used to set the theme for the Monaco editor. This would be a hook but it needs to be placed between
+ * ThemeProvider and the layout page so a component is the most convenient way to do this.
+ */
+export const MonacoThemeProvider = () => {
+ const monaco = useMonaco()
+ const { resolvedTheme } = useTheme()
+
+ // Define the supabase theme for Monaco before anything is rendered. Using useEffect would sometime load the theme
+ // after the editor was loaded, so it looked off. useMemo will always be run before rendering
+ useMemo(() => {
+ if (monaco && resolvedTheme) {
+ const mode = getTheme(resolvedTheme)
+ monaco.editor.defineTheme('supabase', mode)
+ }
+ }, [resolvedTheme, monaco])
+
+ return null
+}
diff --git a/apps/studio/components/interfaces/Auth/AdvancedAuthSettingsForm/AdvancedAuthSettingsForm.tsx b/apps/studio/components/interfaces/Auth/AdvancedAuthSettingsForm/AdvancedAuthSettingsForm.tsx
index 1cd50cd0cbb..44a2ef5f487 100644
--- a/apps/studio/components/interfaces/Auth/AdvancedAuthSettingsForm/AdvancedAuthSettingsForm.tsx
+++ b/apps/studio/components/interfaces/Auth/AdvancedAuthSettingsForm/AdvancedAuthSettingsForm.tsx
@@ -1,7 +1,6 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { useEffect } from 'react'
import { toast } from 'sonner'
-import { boolean, number, object, string } from 'yup'
+import { number, object } from 'yup'
import { useParams } from 'common'
import { FormActions } from 'components/ui/Forms/FormActions'
@@ -16,7 +15,6 @@ import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-que
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { IS_PLATFORM } from 'lib/constants'
-import FormField from '../AuthProvidersForm/FormField'
import {
AlertDescription_Shadcn_,
@@ -24,62 +22,15 @@ import {
Alert_Shadcn_,
Form,
InputNumber,
- Toggle,
WarningIcon,
} from 'ui'
const schema = object({
- JWT_EXP: number()
- .max(604800, 'Must be less than 604800')
- .required('Must have a JWT expiry value'),
- REFRESH_TOKEN_ROTATION_ENABLED: boolean().required(),
- SECURITY_REFRESH_TOKEN_REUSE_INTERVAL: number()
- .min(0, 'Must be a value more than 0')
- .required('Must have a Reuse Interval value'),
- MFA_PHONE_OTP_LENGTH: number()
- .min(6, 'Must be a value 6 or larger')
- .max(30, 'must be a value no greater than 30'),
- MFA_PHONE_TEMPLATE: string().required('SMS template is required.'),
- MFA_MAX_ENROLLED_FACTORS: number()
- .min(0, 'Must be be a value more than 0')
- .max(30, 'Must be a value no greater than 30'),
- DB_MAX_POOL_SIZE: number()
- .min(1, 'Must be 1 or larger')
- .max(200, 'Must be a value no greater than 200'),
API_MAX_REQUEST_DURATION: number()
.min(5, 'Must be 5 or larger')
.max(30, 'Must be a value no greater than 30'),
- MFA_TOTP: string().required(),
- MFA_PHONE: string().required(),
})
-function determineMFAStatus(verifyEnabled: boolean, enrollEnabled: boolean) {
- return verifyEnabled ? (enrollEnabled ? 'Enabled' : 'Verify Enabled') : 'Disabled'
-}
-
-const MFAFactorSelectionOptions = [
- {
- label: 'Enabled',
- value: 'Enabled',
- },
- {
- label: 'Verify Enabled',
- value: 'Verify Enabled',
- },
- {
- label: 'Disabled',
- value: 'Disabled',
- },
-]
-
-const MfaStatusToState = (status: (typeof MFAFactorSelectionOptions)[number]['value']) => {
- return status === 'Enabled'
- ? { verifyEnabled: true, enrollEnabled: true }
- : status === 'Verify Enabled'
- ? { verifyEnabled: true, enrollEnabled: false }
- : { verifyEnabled: false, enrollEnabled: false }
-}
-
const AdvancedAuthSettingsForm = () => {
const { ref: projectRef } = useParams()
const {
@@ -102,72 +53,16 @@ const AdvancedAuthSettingsForm = () => {
const isTeamsEnterprisePlan =
isSuccessSubscription && subscription.plan.id !== 'free' && subscription.plan.id !== 'pro'
- const isProPlanAndUp = isSuccessSubscription && subscription?.plan?.id !== 'free'
- const promptProPlanUpgrade = IS_PLATFORM && !isProPlanAndUp
-
- const projectAddons = subscription?.project_addons.find((addon) => addon.ref === projectRef)
- const hasPurchasedAuthMFAAddOn = projectAddons?.addons.some(
- (addon) => addon.type === 'auth_mfa_phone'
- )
const promptTeamsEnterpriseUpgrade = IS_PLATFORM && !isTeamsEnterprisePlan
const INITIAL_VALUES = {
- SITE_URL: authConfig?.SITE_URL,
- JWT_EXP: authConfig?.JWT_EXP,
- REFRESH_TOKEN_ROTATION_ENABLED: authConfig?.REFRESH_TOKEN_ROTATION_ENABLED || false,
- MFA_PHONE_OTP_LENGTH: authConfig?.MFA_PHONE_OTP_LENGTH || 6,
- MFA_PHONE_TEMPLATE: authConfig?.MFA_PHONE_TEMPLATE || 'Your code is {{ .Code }}',
- SECURITY_REFRESH_TOKEN_REUSE_INTERVAL: authConfig?.SECURITY_REFRESH_TOKEN_REUSE_INTERVAL,
- MFA_MAX_ENROLLED_FACTORS: authConfig?.MFA_MAX_ENROLLED_FACTORS || 10,
- DB_MAX_POOL_SIZE: authConfig?.DB_MAX_POOL_SIZE || 10,
API_MAX_REQUEST_DURATION: authConfig?.API_MAX_REQUEST_DURATION || 10,
- // TOTP is enabled by default. Auth environment variables are distinct from UI state - we use MFA_TOTP and MFA_PHONE to hold the derivedUI state.
- // MFA_TOTP_VERIFY_ENABLED and MFA_TOTP_ENROLL_ENABLED -> Enabled
- // MFA_TOTP_VERIFY_ENABLED and !MFA_TOTP_ENROLL_ENABLED -> Verify Enabled
- // !MFA_TOTP_VERIFY_ENABLED and !MFA_TOTP_ENROLL_ENABLED -> Disabled
- MFA_TOTP:
- determineMFAStatus(
- authConfig?.MFA_TOTP_VERIFY_ENABLED ?? true,
- authConfig?.MFA_TOTP_ENROLL_ENABLED ?? true
- ) || 'Enabled',
- MFA_PHONE:
- determineMFAStatus(
- authConfig?.MFA_PHONE_VERIFY_ENABLED || false,
- authConfig?.MFA_PHONE_ENROLL_ENABLED || false
- ) || 'Disabled',
}
- // For now, we support Twilio and Vonage. Twilio Verify is not supported and the remaining providers are community maintained.
- const sendSMSHookIsEnabled =
- authConfig?.HOOK_SEND_SMS_URI !== null && authConfig?.HOOK_SEND_SMS_ENABLED === true
- const hasValidMFAPhoneProvider = authConfig?.EXTERNAL_PHONE_ENABLED === true
- const hasValidMFAProvider = hasValidMFAPhoneProvider || sendSMSHookIsEnabled
-
const onSubmit = (values: any, { resetForm }: any) => {
- let payload = { ...values }
- const { verifyEnabled: MFA_TOTP_VERIFY_ENABLED, enrollEnabled: MFA_TOTP_ENROLL_ENABLED } =
- MfaStatusToState(values.MFA_TOTP)
- // MFA (Phone) is only available on Pro Plans and up. We translate the UI state, MFA_PHONE and MFA_TOTP into the underlying
- // Auth config state - MFA_PHONE_*_ENABLED and MFA_TOTP_*_ENABLED.
- if (isProPlanAndUp) {
- const { verifyEnabled: MFA_PHONE_VERIFY_ENABLED, enrollEnabled: MFA_PHONE_ENROLL_ENABLED } =
- MfaStatusToState(values.MFA_PHONE)
- payload = {
- ...payload,
- MFA_PHONE_ENROLL_ENABLED,
- MFA_PHONE_VERIFY_ENABLED,
- }
- }
- payload = {
- ...payload,
- MFA_TOTP_ENROLL_ENABLED,
- MFA_TOTP_VERIFY_ENABLED,
- }
- delete payload.MFA_TOTP
- delete payload.MFA_PHONE
+ const payload = { ...values }
if (!isTeamsEnterprisePlan) {
- delete payload.DB_MAX_POOL_SIZE
delete payload.API_MAX_REQUEST_DURATION
}
@@ -201,27 +96,11 @@ const AdvancedAuthSettingsForm = () => {
return (
}
>
-
Access Tokens (JWT)}>
-
- seconds}
- disabled={!canUpdateConfig}
- />
-
-
-
Refresh Tokens}>
-
-
- {values.REFRESH_TOKEN_ROTATION_ENABLED && (
- seconds}
- disabled={!canUpdateConfig}
- />
- )}
-
-
-
Multi-Factor Authentication (MFA)}
- >
-
-
-
- factors}
- disabled={!canUpdateConfig}
- />
-
-
-
Advanced MFA}>
-
- {promptProPlanUpgrade && (
-
- )}
-
- {!hasValidMFAProvider && phoneMFAIsEnabled && (
-
-
-
- To use MFA with Phone you should set up a Phone provider or Send SMS Hook.
-
-
- )}
-
-
-
- {hasUpgradedPhoneMFA && (
-
-
-
- Enabling advanced MFA with phone will result in an additional charge of $75
- per month for the first project in the organization and an additional $10
- per month for additional projects.
-
-
- )}
-
-
-
-
Max Direct Database Connections}
- >
-
- {promptTeamsEnterpriseUpgrade && (
-
- )}
-
- connections}
- disabled={!canUpdateConfig || !isTeamsEnterprisePlan}
- />
-
-
-
Max Request Duration}>
{promptTeamsEnterpriseUpgrade && (
diff --git a/apps/studio/components/interfaces/Auth/Auth.constants.ts b/apps/studio/components/interfaces/Auth/Auth.constants.ts
index a970de63739..27458458bc6 100644
--- a/apps/studio/components/interfaces/Auth/Auth.constants.ts
+++ b/apps/studio/components/interfaces/Auth/Auth.constants.ts
@@ -41,3 +41,6 @@ export function urlRegex(
'i'
)
}
+
+// Use a const string to represent no chars option. Represented as empty string on the backend side.
+export const NO_REQUIRED_CHARACTERS = 'NO_REQUIRED_CHARS'
diff --git a/apps/studio/components/interfaces/Auth/AuthProvidersForm/ProviderForm.tsx b/apps/studio/components/interfaces/Auth/AuthProvidersForm/ProviderForm.tsx
index 9f2f3e5f416..1d7910cc620 100644
--- a/apps/studio/components/interfaces/Auth/AuthProvidersForm/ProviderForm.tsx
+++ b/apps/studio/components/interfaces/Auth/AuthProvidersForm/ProviderForm.tsx
@@ -6,6 +6,7 @@ import ReactMarkdown from 'react-markdown'
import { toast } from 'sonner'
import { useParams } from 'common'
+import { Markdown } from 'components/interfaces/Markdown'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DocsButton } from 'components/ui/DocsButton'
import type { components } from 'data/api'
@@ -24,11 +25,11 @@ import {
Input,
WarningIcon,
} from 'ui'
+import { Admonition } from 'ui-patterns'
+import { NO_REQUIRED_CHARACTERS } from '../Auth.constants'
import { ProviderCollapsibleClasses } from './AuthProvidersForm.constants'
import type { Provider } from './AuthProvidersForm.types'
import FormField from './FormField'
-import { Markdown } from 'components/interfaces/Markdown'
-import { Admonition } from 'ui-patterns'
export interface ProviderFormProps {
config: components['schemas']['GoTrueConfigResponse']
@@ -175,6 +176,11 @@ const ProviderForm = ({ config, provider }: ProviderFormProps) => {
if (payload[x] === '') payload[x] = null
})
+ // The backend uses empty string to represent no required characters in the password
+ if (payload.PASSWORD_REQUIRED_CHARACTERS === NO_REQUIRED_CHARACTERS) {
+ payload.PASSWORD_REQUIRED_CHARACTERS = ''
+ }
+
updateAuthConfig(
{ projectRef: projectRef!, config: payload },
{
diff --git a/apps/studio/components/interfaces/Auth/AuthProvidersFormValidation.tsx b/apps/studio/components/interfaces/Auth/AuthProvidersFormValidation.tsx
index dcb72820e2a..9cd4c2e2ca7 100644
--- a/apps/studio/components/interfaces/Auth/AuthProvidersFormValidation.tsx
+++ b/apps/studio/components/interfaces/Auth/AuthProvidersFormValidation.tsx
@@ -1,6 +1,6 @@
-import { boolean, number, object, string } from 'yup'
-import { urlRegex } from 'components/interfaces/Auth/Auth.constants'
+import { NO_REQUIRED_CHARACTERS, urlRegex } from 'components/interfaces/Auth/Auth.constants'
import { ProjectAuthConfigData } from 'data/auth/auth-config-query'
+import { boolean, number, object, string } from 'yup'
const parseBase64URL = (b64url: string) => {
return atob(b64url.replace(/[-]/g, '+').replace(/[_]/g, '/'))
@@ -36,6 +36,44 @@ const PROVIDER_EMAIL = {
If disabled, a user can change their password at any time.`,
type: 'boolean',
},
+ PASSWORD_HIBP_ENABLED: {
+ title: 'Prevent use of leaked passwords',
+ description:
+ 'Rejects the use of known or easy to guess passwords on sign up or password change. Powered by the HaveIBeenPwned.org Pwned Passwords API.',
+ type: 'boolean',
+ },
+ PASSWORD_MIN_LENGTH: {
+ title: 'Minimum password length',
+ type: 'number',
+ description:
+ 'Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more.',
+ units: 'characters',
+ },
+ PASSWORD_REQUIRED_CHARACTERS: {
+ type: 'select',
+ title: 'Password Requirements',
+ description: 'Passwords that do not have at least one of each will be rejected as weak.',
+ enum: [
+ {
+ label: 'No required characters (default)',
+ value: NO_REQUIRED_CHARACTERS,
+ },
+ {
+ label: 'Letters and digits',
+ value: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789',
+ },
+ {
+ label: 'Lowercase, uppercase letters and digits',
+ value: 'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789',
+ },
+ {
+ label: 'Lowercase, uppercase letters, digits and symbols (recommended)',
+ value:
+ 'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};\'\\\\:"|<>?,./`~',
+ },
+ ],
+ },
+
MAILER_OTP_EXP: {
title: 'Email OTP Expiration',
type: 'number',
@@ -55,6 +93,9 @@ const PROVIDER_EMAIL = {
.max(86400, 'Must be no more than 86400')
.required('This is required'),
MAILER_OTP_LENGTH: number().min(6, 'Must be at least 6').max(10, 'Must be no more than 10'),
+ PASSWORD_MIN_LENGTH: number()
+ .min(6, 'Must be greater or equal to 6.')
+ .required('This is required'),
}),
misc: {
iconKey: 'email-icon2',
diff --git a/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx b/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx
index 6ddf1eafef7..1e1c108669c 100644
--- a/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx
+++ b/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx
@@ -1,77 +1,38 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { ExternalLink, Eye, EyeOff } from 'lucide-react'
+import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
-import { useEffect, useState } from 'react'
+import { useEffect } from 'react'
import { toast } from 'sonner'
-import { boolean, number, object, string } from 'yup'
+import { boolean, object, string } from 'yup'
import { useParams } from 'common'
import { Markdown } from 'components/interfaces/Markdown'
import { FormActions } from 'components/ui/Forms/FormActions'
+import { FormHeader } from 'components/ui/Forms/FormHeader'
import { FormPanel } from 'components/ui/Forms/FormPanel'
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection'
import NoPermission from 'components/ui/NoPermission'
-import UpgradeToPro from 'components/ui/UpgradeToPro'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
-import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
-import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
-import { IS_PLATFORM } from 'lib/constants'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Button,
Form,
- Input,
- InputNumber,
Toggle,
WarningIcon,
} from 'ui'
-import FormField from '../AuthProvidersForm/FormField'
-
-// Use a const string to represent no chars option. Represented as empty string on the backend side.
-const NO_REQUIRED_CHARACTERS = 'NO_REQUIRED_CHARS'
-const LETTERS_AND_DIGITS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789'
-const LOWER_UPPER_DIGITS = 'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789'
-const LOWER_UPPER_DIGITS_SYMBOLS = LOWER_UPPER_DIGITS + ':!@#$%^&*()_+-=[]{};\'\\\\:"|<>?,./`~'
+import { NO_REQUIRED_CHARACTERS } from '../Auth.constants'
const schema = object({
DISABLE_SIGNUP: boolean().required(),
EXTERNAL_ANONYMOUS_USERS_ENABLED: boolean().required(),
SECURITY_MANUAL_LINKING_ENABLED: boolean().required(),
SITE_URL: string().required('Must have a Site URL'),
- SECURITY_CAPTCHA_ENABLED: boolean().required(),
- SECURITY_CAPTCHA_SECRET: string().when('SECURITY_CAPTCHA_ENABLED', {
- is: true,
- then: (schema) => schema.required('Must have a Captcha secret'),
- }),
- SECURITY_CAPTCHA_PROVIDER: string().when('SECURITY_CAPTCHA_ENABLED', {
- is: true,
- then: (schema) =>
- schema
- .oneOf(['hcaptcha', 'turnstile'])
- .required('Captcha provider must be either hcaptcha or turnstile'),
- }),
- SESSIONS_TIMEBOX: number().min(0, 'Must be a positive number'),
- SESSIONS_INACTIVITY_TIMEOUT: number().min(0, 'Must be a positive number'),
- SESSIONS_SINGLE_PER_USER: boolean(),
- PASSWORD_MIN_LENGTH: number().min(6, 'Must be greater or equal to 6.'),
- PASSWORD_REQUIRED_CHARACTERS: string(),
- PASSWORD_HIBP_ENABLED: boolean(),
})
-function HoursOrNeverText({ value }: { value: number }) {
- if (value === 0) {
- return 'never'
- } else if (value === 1) {
- return 'hour'
- } else {
- return 'hours'
- }
-}
-
const formId = 'auth-config-basic-settings'
const BasicAuthSettingsForm = () => {
@@ -85,36 +46,14 @@ const BasicAuthSettingsForm = () => {
} = useAuthConfigQuery({ projectRef })
const { mutate: updateAuthConfig, isLoading: isUpdatingConfig } = useAuthConfigUpdateMutation()
- const [hidden, setHidden] = useState(true)
const canReadConfig = useCheckPermissions(PermissionAction.READ, 'custom_config_gotrue')
const canUpdateConfig = useCheckPermissions(PermissionAction.UPDATE, 'custom_config_gotrue')
- const organization = useSelectedOrganization()
- const { data: subscription, isSuccess: isSuccessSubscription } = useOrgSubscriptionQuery(
- {
- orgSlug: organization?.slug,
- },
- { enabled: IS_PLATFORM }
- )
-
- const isProPlanAndUp = isSuccessSubscription && subscription?.plan?.id !== 'free'
- const promptProPlanUpgrade = IS_PLATFORM && !isProPlanAndUp
-
const INITIAL_VALUES = {
DISABLE_SIGNUP: !authConfig?.DISABLE_SIGNUP,
EXTERNAL_ANONYMOUS_USERS_ENABLED: authConfig?.EXTERNAL_ANONYMOUS_USERS_ENABLED,
SECURITY_MANUAL_LINKING_ENABLED: authConfig?.SECURITY_MANUAL_LINKING_ENABLED || false,
SITE_URL: authConfig?.SITE_URL,
- SECURITY_CAPTCHA_ENABLED: authConfig?.SECURITY_CAPTCHA_ENABLED || false,
- SECURITY_CAPTCHA_SECRET: authConfig?.SECURITY_CAPTCHA_SECRET || '',
- SECURITY_CAPTCHA_PROVIDER: authConfig?.SECURITY_CAPTCHA_PROVIDER || 'hcaptcha',
- SESSIONS_TIMEBOX: authConfig?.SESSIONS_TIMEBOX || 0,
- SESSIONS_INACTIVITY_TIMEOUT: authConfig?.SESSIONS_INACTIVITY_TIMEOUT || 0,
- SESSIONS_SINGLE_PER_USER: authConfig?.SESSIONS_SINGLE_PER_USER || false,
- PASSWORD_MIN_LENGTH: authConfig?.PASSWORD_MIN_LENGTH || 6,
- PASSWORD_REQUIRED_CHARACTERS:
- authConfig?.PASSWORD_REQUIRED_CHARACTERS || NO_REQUIRED_CHARACTERS,
- PASSWORD_HIBP_ENABLED: authConfig?.PASSWORD_HIBP_ENABLED || false,
}
const onSubmit = (values: any, { resetForm }: any) => {
@@ -154,114 +93,125 @@ const BasicAuthSettingsForm = () => {
}
return (
-
-
- >
- )
- }}
-
+
+
+
+ >
+ )
+ }}
+
+