diff --git a/src/components/settings/SecurityTab.tsx b/src/components/settings/SecurityTab.tsx index 846b6c95..7efd12f8 100644 --- a/src/components/settings/SecurityTab.tsx +++ b/src/components/settings/SecurityTab.tsx @@ -5,7 +5,7 @@ import { Separator } from '@/components/ui/separator'; import { Badge } from '@/components/ui/badge'; import { useToast } from '@/hooks/use-toast'; import { useAuth } from '@/hooks/useAuth'; -import { Shield, Key, Smartphone, Globe, Loader2 } from 'lucide-react'; +import { Shield, Key, Smartphone, Globe, Loader2, RefreshCw } from 'lucide-react'; import { TOTPSetup } from '@/components/auth/TOTPSetup'; import { GoogleIcon } from '@/components/icons/GoogleIcon'; import { DiscordIcon } from '@/components/icons/DiscordIcon'; @@ -127,6 +127,17 @@ export function SecurityTab() { // Refresh identities - should now include email provider after waiting in addPasswordToAccount await loadIdentities(); + // Check if password was actually verified + if (!hasPassword && addPasswordMode === 'standalone') { + // Password addition failed verification + toast({ + title: 'Verification Failed', + description: 'Password was set but identity verification failed. Please refresh the page and check your Security settings.', + variant: 'destructive' + }); + return; // Don't close dialog or clear provider + } + if (addPasswordMode === 'disconnect' && passwordSetupProvider) { toast({ title: "Password Set", @@ -146,6 +157,19 @@ export function SecurityTab() { } }; + const handleRefreshIdentities = async () => { + setLoadingIdentities(true); + await loadIdentities(); + setLoadingIdentities(false); + + toast({ + title: 'Identities Refreshed', + description: hasPassword + ? 'Your password authentication is now active.' + : 'Identity status updated.', + }); + }; + const handleAddPassword = () => { setAddPasswordMode('standalone'); setPasswordSetupProvider('google' as OAuthProvider); @@ -206,22 +230,33 @@ export function SecurityTab() { - {hasPassword ? ( - setPasswordDialogOpen(true)}> - Change Password + + {hasPassword ? ( + setPasswordDialogOpen(true)}> + Change Password + + ) : ( + + {addingPassword ? ( + <> + + Adding Password... + > + ) : ( + 'Add Password' + )} + + )} + + - ) : ( - - {addingPassword ? ( - <> - - Adding Password... - > - ) : ( - 'Add Password' - )} - - )} + diff --git a/src/lib/identityService.ts b/src/lib/identityService.ts index 68d69524..4fc09fec 100644 --- a/src/lib/identityService.ts +++ b/src/lib/identityService.ts @@ -164,22 +164,26 @@ export async function connectIdentity( * Wait for email provider to be created after password addition * Supabase takes time to create the email identity, so we poll with retries */ -async function waitForEmailProvider(maxRetries = 4): Promise { - const delays = [500, 1000, 1500, 2000]; // Exponential backoff +async function waitForEmailProvider(maxRetries = 6): Promise { + const delays = [500, 1000, 1500, 2000, 2500, 3000]; // ~10.5s total for (let i = 0; i < maxRetries; i++) { const identities = await getUserIdentities(); const hasEmail = identities.some(id => id.provider === 'email'); if (hasEmail) { + console.log(`[IdentityService] Email provider found after ${i + 1} attempts`); return true; } + console.log(`[IdentityService] Email provider not found, attempt ${i + 1}/${maxRetries}`); + if (i < maxRetries - 1) { await new Promise(resolve => setTimeout(resolve, delays[i])); } } + console.error('[IdentityService] Email provider not found after max retries'); return false; } @@ -213,7 +217,10 @@ export async function addPasswordToAccount( const emailCreated = await waitForEmailProvider(); if (!emailCreated) { - console.warn('[IdentityService] Email provider not found after password addition'); + return { + success: false, + error: 'Password was set but email provider verification failed. Please refresh the page and try signing in with your email and password.' + }; } // Log audit event