mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 21:51:12 -05:00
Fix orphaned password state
This commit is contained in:
@@ -223,9 +223,16 @@ export function SecurityTab() {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
{hasPassword ? (
|
{hasPassword ? (
|
||||||
<>Update your password to keep your account secure. {user?.identities?.some(i => i.provider === 'totp') && 'Two-factor authentication will be required.'}</>
|
<>Update your password to keep your account secure.</>
|
||||||
) : (
|
) : (
|
||||||
'Add password authentication to your account for increased security and backup access.'
|
<>
|
||||||
|
Add password authentication to your account for increased security and backup access.
|
||||||
|
{identities.length > 0 && (
|
||||||
|
<span className="block mt-2 text-amber-600 dark:text-amber-400">
|
||||||
|
If you've previously set a password but don't see it here, click "Add Password" to re-verify your authentication.
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|||||||
@@ -189,6 +189,7 @@ async function waitForEmailProvider(maxRetries = 6): Promise<boolean> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Add password authentication to an OAuth-only account
|
* Add password authentication to an OAuth-only account
|
||||||
|
* Also handles re-creating email identity for orphaned passwords
|
||||||
*/
|
*/
|
||||||
export async function addPasswordToAccount(
|
export async function addPasswordToAccount(
|
||||||
password: string
|
password: string
|
||||||
@@ -202,7 +203,7 @@ export async function addPasswordToAccount(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update user with password
|
// Update user with password (works for both new and existing passwords)
|
||||||
const { error } = await supabase.auth.updateUser({ password });
|
const { error } = await supabase.auth.updateUser({ password });
|
||||||
|
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
@@ -217,6 +218,34 @@ export async function addPasswordToAccount(
|
|||||||
const emailCreated = await waitForEmailProvider();
|
const emailCreated = await waitForEmailProvider();
|
||||||
|
|
||||||
if (!emailCreated) {
|
if (!emailCreated) {
|
||||||
|
// Password was set but identity verification failed
|
||||||
|
// Try one more aggressive approach: sign in with the new password
|
||||||
|
console.log('[IdentityService] Attempting sign-in to trigger identity creation');
|
||||||
|
|
||||||
|
const { data: { user } } = await supabase.auth.getUser();
|
||||||
|
if (user?.email) {
|
||||||
|
// Attempt to sign in (this might create the identity)
|
||||||
|
const { error: signInError } = await supabase.auth.signInWithPassword({
|
||||||
|
email: user.email,
|
||||||
|
password: password
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!signInError) {
|
||||||
|
// Sign-in successful, check identities again
|
||||||
|
const retriedEmailCreated = await waitForEmailProvider(2); // Quick retry
|
||||||
|
if (retriedEmailCreated) {
|
||||||
|
console.log('[IdentityService] Email provider created after sign-in');
|
||||||
|
|
||||||
|
// Log audit event
|
||||||
|
await logIdentityChange(user.id, 'password_added', {
|
||||||
|
method: 'oauth_fallback_with_signin_retry'
|
||||||
|
});
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Password was set but email provider verification failed. Please refresh the page and try signing in with your email and password.'
|
error: 'Password was set but email provider verification failed. Please refresh the page and try signing in with your email and password.'
|
||||||
@@ -241,6 +270,54 @@ export async function addPasswordToAccount(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if user has an orphaned password (password exists but no email identity)
|
||||||
|
*/
|
||||||
|
export async function hasOrphanedPassword(): Promise<boolean> {
|
||||||
|
const identities = await getUserIdentities();
|
||||||
|
const hasEmailIdentity = identities.some(i => i.provider === 'email');
|
||||||
|
|
||||||
|
if (hasEmailIdentity) return false;
|
||||||
|
|
||||||
|
// If user has OAuth identities but no email identity, they might have an orphaned password
|
||||||
|
return identities.length > 0 && !hasEmailIdentity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-verify password authentication by attempting sign-in
|
||||||
|
* This forces Supabase to create the email identity if it's missing
|
||||||
|
*/
|
||||||
|
export async function reverifyPasswordAuth(
|
||||||
|
email: string,
|
||||||
|
password: string
|
||||||
|
): Promise<IdentityOperationResult> {
|
||||||
|
try {
|
||||||
|
const { error } = await supabase.auth.signInWithPassword({
|
||||||
|
email,
|
||||||
|
password
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
|
|
||||||
|
// Check if email identity was created
|
||||||
|
const emailCreated = await waitForEmailProvider(3);
|
||||||
|
|
||||||
|
if (!emailCreated) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Sign-in successful but identity verification failed. Please contact support.'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
} catch (error: any) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message || 'Failed to verify password authentication'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log identity changes to audit log
|
* Log identity changes to audit log
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user