Refactor: Secure email confirmation flow

This commit is contained in:
gpt-engineer-app[bot]
2025-10-01 15:50:27 +00:00
parent ba04dbc966
commit 5634dc4920
2 changed files with 65 additions and 12 deletions

View File

@@ -21,6 +21,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const [profile, setProfile] = useState<Profile | null>(null);
const [loading, setLoading] = useState(true);
const [pendingEmail, setPendingEmail] = useState<string | null>(null);
const [previousEmail, setPreviousEmail] = useState<string | null>(null);
const fetchProfile = async (userId: string) => {
try {
@@ -61,13 +62,72 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
// Listen for auth changes
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((event, session) => {
} = supabase.auth.onAuthStateChange(async (event, session) => {
const currentEmail = session?.user?.email;
const newEmailPending = session?.user?.new_email;
setSession(session);
setUser(session?.user ?? null);
// Track pending email changes
const newEmail = session?.user?.new_email;
setPendingEmail(newEmail ?? null);
setPendingEmail(newEmailPending ?? null);
// Detect confirmed email change: email changed AND no longer pending
if (
session?.user &&
previousEmail &&
currentEmail &&
currentEmail !== previousEmail &&
!newEmailPending
) {
console.log('Email change confirmed:', { from: previousEmail, to: currentEmail });
// Defer Novu update and notifications to avoid blocking auth
setTimeout(async () => {
try {
// Update Novu subscriber with confirmed email
const { notificationService } = await import('@/lib/notificationService');
if (notificationService.isEnabled()) {
await notificationService.updateSubscriber({
subscriberId: session.user.id,
email: currentEmail,
});
}
// Log the confirmed email change
await supabase.from('admin_audit_log').insert({
admin_user_id: session.user.id,
target_user_id: session.user.id,
action: 'email_change_completed',
details: {
old_email: previousEmail,
new_email: currentEmail,
timestamp: new Date().toISOString(),
},
});
// Send final security notification
if (notificationService.isEnabled()) {
await notificationService.trigger({
workflowId: 'email-changed',
subscriberId: session.user.id,
payload: {
oldEmail: previousEmail,
newEmail: currentEmail,
timestamp: new Date().toISOString(),
},
});
}
} catch (error) {
console.error('Error updating Novu after email confirmation:', error);
}
}, 0);
}
// Update tracked email
if (currentEmail) {
setPreviousEmail(currentEmail);
}
if (session?.user) {
// Defer profile fetch to avoid deadlock