feat: Implement password reset flow

This commit is contained in:
gpt-engineer-app[bot]
2025-10-14 17:14:34 +00:00
parent e8a7c028e9
commit 70f94b8a30
4 changed files with 166 additions and 73 deletions

View File

@@ -213,35 +213,38 @@ export async function addPasswordToAccount(
};
}
// Step 1: Set the password (does NOT create email identity yet)
console.log('[IdentityService] Setting password');
const { error: updateError } = await supabase.auth.updateUser({ password });
if (updateError) throw updateError;
// Step 2: Trigger signup confirmation email (this creates the email identity)
console.log('[IdentityService] Sending signup confirmation email');
const { error: resendError } = await supabase.auth.resend({
type: 'signup',
email: userEmail,
options: {
emailRedirectTo: `${window.location.origin}/auth?confirmed=password-setup`
console.log('[IdentityService] Initiating password setup via reset flow');
// Step 1: Store the desired password temporarily in session storage
// This will be used after clicking the reset link
sessionStorage.setItem('pending_password_setup', password);
console.log('[IdentityService] Stored pending password in session storage');
// Step 2: Trigger Supabase password reset email
// This creates the email identity when user clicks link and sets password
const { error: resetError } = await supabase.auth.resetPasswordForEmail(
userEmail,
{
redirectTo: `${window.location.origin}/auth/callback?action=password-setup`
}
});
if (resendError) {
console.error('[IdentityService] Failed to send confirmation email:', resendError);
throw resendError;
);
if (resetError) {
console.error('[IdentityService] Failed to send password reset email:', resetError);
sessionStorage.removeItem('pending_password_setup'); // Cleanup on failure
throw resetError;
}
console.log('[IdentityService] Password reset email sent successfully');
// Step 3: Get user profile for custom notification email
console.log('[IdentityService] Fetching user profile');
const { data: profile } = await supabase
.from('profiles')
.select('display_name, username')
.eq('user_id', user!.id)
.single();
// Step 4: Send our custom "Password Added" notification (informational)
// Step 4: Send custom "Password Setup Instructions" email (informational)
console.log('[IdentityService] Sending custom notification email');
try {
await supabase.functions.invoke('send-password-added-email', {
@@ -251,33 +254,32 @@ export async function addPasswordToAccount(
username: profile?.username,
},
});
console.log('[IdentityService] Custom notification email sent');
} catch (emailError) {
console.error('[IdentityService] Custom email failed (non-blocking):', emailError);
// Don't fail the whole operation
}
// Step 5: Log the action
await logIdentityChange(user!.id, 'password_added', {
method: 'oauth_password_addition_with_signup_confirmation',
confirmation_email_sent: true
});
// Step 6: Sign user out (they must confirm via email)
console.log('[IdentityService] Signing user out');
await supabase.auth.signOut();
// Return success with relogin and email confirmation flags
// Step 5: Log the action
await logIdentityChange(user!.id, 'password_setup_initiated', {
method: 'reset_password_flow',
reset_email_sent: true,
timestamp: new Date().toISOString()
});
// Return success - user needs to check email and click reset link
return {
success: true,
needsRelogin: true,
needsEmailConfirmation: true,
email: userEmail
};
} catch (error: any) {
console.error('[IdentityService] Failed to add password:', error);
console.error('[IdentityService] Failed to initiate password setup:', error);
// Cleanup on error
sessionStorage.removeItem('pending_password_setup');
return {
success: false,
error: error.message || 'Failed to set password'
error: error.message || 'Failed to initiate password setup'
};
}
}
@@ -312,23 +314,24 @@ export async function triggerOrphanedPasswordConfirmation(
};
}
console.log('[IdentityService] Resending signup confirmation email');
console.log('[IdentityService] Resending password reset email for orphaned password');
// Send Supabase signup confirmation email
const { error: resendError } = await supabase.auth.resend({
type: 'signup',
email: user.email,
options: {
emailRedirectTo: `${window.location.origin}/auth?confirmed=password-confirmation`
// Send Supabase password reset email
// The user's password is already set in auth.users, they just need to click
// the reset link to create the email identity
const { error: resetError } = await supabase.auth.resetPasswordForEmail(
user.email,
{
redirectTo: `${window.location.origin}/auth/callback?action=confirm-password`
}
});
);
if (resendError) {
console.error('[IdentityService] Failed to resend confirmation:', resendError);
throw resendError;
if (resetError) {
console.error('[IdentityService] Failed to send password reset email:', resetError);
throw resetError;
}
console.log('[IdentityService] Confirmation email resent successfully');
console.log('[IdentityService] Password reset email sent successfully');
// Optional: Get profile for custom notification
const { data: profile } = await supabase
@@ -354,7 +357,7 @@ export async function triggerOrphanedPasswordConfirmation(
await logIdentityChange(user.id, 'orphaned_password_confirmation_triggered', {
method: source || 'manual_button_click',
timestamp: new Date().toISOString(),
confirmation_email_sent: true
reset_email_sent: true
});
return {
@@ -363,10 +366,10 @@ export async function triggerOrphanedPasswordConfirmation(
email: user.email
};
} catch (error: any) {
console.error('[IdentityService] Failed to trigger confirmation:', error);
console.error('[IdentityService] Failed to trigger password reset:', error);
return {
success: false,
error: error.message || 'Failed to trigger email confirmation'
error: error.message || 'Failed to send password reset email'
};
}
}