From b1617109522b34d56ba9bf40ba843b2b05a565fa Mon Sep 17 00:00:00 2001 From: Daniel Arroyo Date: Sat, 15 Aug 2026 03:27:31 -0400 Subject: [PATCH] fix(frontend): post-change-password cookie timing + defensive 401 fallback Dos cambios para destrabar el flujo post-change-password: 1. AuthContext.changePassword: despues de setUser(u), hacer un round-trip a api.me() para confirmar que la nueva cookie ya es utilizable. Sin este paso, React monta AuthenticatedApp tan rapido que el primer fetch desde usePersistedState.load() corre antes de que el browser haya propagado el Set-Cookie (race condition reportado en Chrome). 2. usePersistedState.load: en 401, caer en makeDefaultAppState() en lugar de setStateInternal(null). Antes dejaba al usuario varado en un Cargando tu estado... para siempre. Ahora ve la app vacia con SaveIndicator, y al primer cambio real el auto-save sube el estado al server. --- src/frontend/src/auth/AuthContext.tsx | 5 +++++ src/frontend/src/hooks/usePersistedState.ts | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/auth/AuthContext.tsx b/src/frontend/src/auth/AuthContext.tsx index 13a6654..37f53dd 100644 --- a/src/frontend/src/auth/AuthContext.tsx +++ b/src/frontend/src/auth/AuthContext.tsx @@ -54,6 +54,11 @@ export function AuthProvider({ children }: { children: ReactNode }) { const changePassword = useCallback(async (currentPassword: string, newPassword: string) => { const u = await api.changePassword(currentPassword, newPassword) setUser(u) + // El Set-Cookie de la respuesta a veces no está visible para el fetch + // siguiente en el mismo microtask (Chrome/Firefox). Confirmamos con + // un round-trip al backend antes de soltar el control, asi React + // monta AuthenticatedApp con la cookie ya lista para los fetches internos. + await api.me() }, []) return ( diff --git a/src/frontend/src/hooks/usePersistedState.ts b/src/frontend/src/hooks/usePersistedState.ts index 268ab47..c42d73d 100644 --- a/src/frontend/src/hooks/usePersistedState.ts +++ b/src/frontend/src/hooks/usePersistedState.ts @@ -29,7 +29,8 @@ export function usePersistedState(): UsePersistedStateResult { setStateInternal(loaded ?? makeDefaultAppState()) } catch (err) { if (err instanceof ApiError && err.status === 401) { - setStateInternal(null) + // No bloquear la UI en 401. Cargar default state. + setStateInternal(makeDefaultAppState()) } else { setErrorMessage(err instanceof Error ? err.message : 'Error cargando estado') }