diff --git a/.env b/.env index 38d3973e..40eddd4c 100644 --- a/.env +++ b/.env @@ -16,3 +16,6 @@ VITE_CLOUDFLARE_ACCOUNT_HASH=X-2-mmiWukWxvAQQ2_o-7Q VITE_NOVU_APPLICATION_IDENTIFIER="" VITE_NOVU_SOCKET_URL="wss://ws.novu.co" VITE_NOVU_API_URL="https://api.novu.co" + +# CAPTCHA Bypass (Development/Preview Only) +VITE_ALLOW_CAPTCHA_BYPASS=true diff --git a/.env.example b/.env.example index 710fc644..618c48a2 100644 --- a/.env.example +++ b/.env.example @@ -14,6 +14,12 @@ VITE_TURNSTILE_SITE_KEY=your-turnstile-site-key # Cloudflare Images Configuration VITE_CLOUDFLARE_ACCOUNT_HASH=your-cloudflare-account-hash +# CAPTCHA Bypass Control (Development/Preview Only) +# This acts as a safety gate - even if admins enable bypass in settings, +# it will only work if this is set to 'true' +# MUST be 'false' or unset in production +VITE_ALLOW_CAPTCHA_BYPASS=false + # Novu Configuration # For Novu Cloud, use these defaults: # - Socket URL: wss://ws.novu.co diff --git a/src/components/auth/AuthModal.tsx b/src/components/auth/AuthModal.tsx index 0cfd06db..e7295cf8 100644 --- a/src/components/auth/AuthModal.tsx +++ b/src/components/auth/AuthModal.tsx @@ -10,6 +10,7 @@ import { supabase } from '@/integrations/supabase/client'; import { useToast } from '@/hooks/use-toast'; import { TurnstileCaptcha } from './TurnstileCaptcha'; import { notificationService } from '@/lib/notificationService'; +import { useCaptchaBypass } from '@/hooks/useCaptchaBypass'; interface AuthModalProps { open: boolean; @@ -34,9 +35,7 @@ export function AuthModal({ open, onOpenChange, defaultTab = 'signin' }: AuthMod displayName: '' }); - // Detect iframe environment and make CAPTCHA optional for preview compatibility - const isInIframe = window.self !== window.top; - const requireCaptcha = !isInIframe; + const { requireCaptcha } = useCaptchaBypass(); const handleInputChange = (e: React.ChangeEvent) => { setFormData(prev => ({ diff --git a/src/hooks/useAdminSettings.ts b/src/hooks/useAdminSettings.ts index 63064bdc..d03075c8 100644 --- a/src/hooks/useAdminSettings.ts +++ b/src/hooks/useAdminSettings.ts @@ -78,6 +78,12 @@ export function useAdminSettings() { return settings?.filter(s => s.category === category) || []; }; + const getCaptchaBypassEnabled = (): boolean => { + const value = getSettingValue('auth.captcha_bypass_enabled', 'false'); + const cleanValue = typeof value === 'string' ? value.replace(/"/g, '') : value; + return cleanValue === 'true' || cleanValue === true; + }; + const updateSetting = async (key: string, value: any) => { return updateSettingMutation.mutateAsync({ key, value }); }; @@ -179,5 +185,6 @@ export function useAdminSettings() { getAutoRefreshStrategy, getPreserveInteractionState, getUseRealtimeQueue, + getCaptchaBypassEnabled, }; } \ No newline at end of file diff --git a/src/hooks/useCaptchaBypass.ts b/src/hooks/useCaptchaBypass.ts new file mode 100644 index 00000000..1060f1ef --- /dev/null +++ b/src/hooks/useCaptchaBypass.ts @@ -0,0 +1,34 @@ +import { useEffect } from 'react'; +import { useAdminSettings } from './useAdminSettings'; + +export function useCaptchaBypass() { + const { getSettingValue } = useAdminSettings(); + + // Layer 1: Check if environment allows bypass + const environmentAllowsBypass = import.meta.env.VITE_ALLOW_CAPTCHA_BYPASS === 'true'; + + // Layer 2: Check if admin has enabled bypass + const adminEnabledBypass = getSettingValue('auth.captcha_bypass_enabled', false) === true || + getSettingValue('auth.captcha_bypass_enabled', false) === 'true'; + + // Both layers must allow bypass + const bypassEnabled = environmentAllowsBypass && adminEnabledBypass; + + // Log warning if bypass is active + useEffect(() => { + if (bypassEnabled && typeof window !== 'undefined') { + console.warn( + '⚠️ CAPTCHA BYPASS IS ACTIVE\n' + + 'This should only be enabled in development/preview environments.\n' + + 'Verify VITE_ALLOW_CAPTCHA_BYPASS=false in production!' + ); + } + }, [bypassEnabled]); + + return { + bypassEnabled, + requireCaptcha: !bypassEnabled, + environmentAllowsBypass, + adminEnabledBypass + }; +} diff --git a/src/pages/AdminSettings.tsx b/src/pages/AdminSettings.tsx index 137fd2be..9b504d86 100644 --- a/src/pages/AdminSettings.tsx +++ b/src/pages/AdminSettings.tsx @@ -13,7 +13,7 @@ import { useUserRole } from '@/hooks/useUserRole'; import { useAdminSettings } from '@/hooks/useAdminSettings'; import { NovuMigrationUtility } from '@/components/admin/NovuMigrationUtility'; import { TestDataGenerator } from '@/components/admin/TestDataGenerator'; -import { Loader2, Save, Clock, Users, Bell, Shield, Settings, Trash2, Plug } from 'lucide-react'; +import { Loader2, Save, Clock, Users, Bell, Shield, Settings, Trash2, Plug, AlertTriangle, Lock } from 'lucide-react'; export default function AdminSettings() { const { user } = useAuth(); @@ -24,7 +24,8 @@ export default function AdminSettings() { error, updateSetting, isUpdating, - getSettingsByCategory + getSettingsByCategory, + getCaptchaBypassEnabled } = useAdminSettings(); if (roleLoading || isLoading) { @@ -440,6 +441,10 @@ export default function AdminSettings() { Moderation + + + Auth + Users @@ -488,6 +493,50 @@ export default function AdminSettings() { + + + + + + Authentication Settings + + + Configure authentication security, CAPTCHA, and login settings + + + + {getCaptchaBypassEnabled() && ( + + +
+ +
+

+ CAPTCHA Bypass is Currently Enabled +

+

+ Authentication requests will not require CAPTCHA verification. + This should ONLY be enabled in development environments. +

+
+
+
+
+ )} + {getSettingsByCategory('auth').length > 0 ? ( + getSettingsByCategory('auth').map((setting) => ( + + )) + ) : ( +
+ +

No authentication settings configured yet.

+
+ )} +
+
+
+ diff --git a/supabase/migrations/20251011003904_dbbdcddf-b9b8-4a45-8cf9-434f8b333e1a.sql b/supabase/migrations/20251011003904_dbbdcddf-b9b8-4a45-8cf9-434f8b333e1a.sql new file mode 100644 index 00000000..d8a79e42 --- /dev/null +++ b/supabase/migrations/20251011003904_dbbdcddf-b9b8-4a45-8cf9-434f8b333e1a.sql @@ -0,0 +1,9 @@ +-- Add CAPTCHA bypass setting to admin_settings +INSERT INTO public.admin_settings (setting_key, setting_value, category, description) +VALUES ( + 'auth.captcha_bypass_enabled', + 'false', + 'auth', + 'Allow CAPTCHA bypass for authentication (development only - requires VITE_ALLOW_CAPTCHA_BYPASS=true in environment)' +) +ON CONFLICT (setting_key) DO NOTHING; \ No newline at end of file