From e44c0d00a3247e8f65702c1449ffe8b1cdd80cb0 Mon Sep 17 00:00:00 2001 From: Thomas Marchand Date: Sat, 3 Jan 2026 08:33:31 +0000 Subject: [PATCH] Fix mission not loading when accessed via URL before authentication When loading a mission via URL param (?mission=...), the initial API fetch would fail with 401 before the user authenticated. After login, nothing triggered a re-fetch of the mission data. Added auth retry mechanism: - Add signalAuthSuccess() to dispatch event after successful login - Add authRetryTrigger state and listener in control-client - Re-fetch mission and providers when auth succeeds --- dashboard/src/app/control/control-client.tsx | 24 ++++++++++++++++---- dashboard/src/components/auth-gate.tsx | 3 ++- dashboard/src/lib/auth.ts | 5 ++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/dashboard/src/app/control/control-client.tsx b/dashboard/src/app/control/control-client.tsx index d830d21..7d43afb 100644 --- a/dashboard/src/app/control/control-client.tsx +++ b/dashboard/src/app/control/control-client.tsx @@ -864,7 +864,18 @@ export default function ControlClient() { }); }, []); - // Load mission from URL param on mount + // Load mission from URL param on mount (and retry on auth success) + const [authRetryTrigger, setAuthRetryTrigger] = useState(0); + + // Listen for auth success to retry loading + useEffect(() => { + const onAuthSuccess = () => { + setAuthRetryTrigger((prev) => prev + 1); + }; + window.addEventListener("openagent:auth:success", onAuthSuccess); + return () => window.removeEventListener("openagent:auth:success", onAuthSuccess); + }, []); + useEffect(() => { const missionId = searchParams.get("mission"); if (missionId) { @@ -877,7 +888,10 @@ export default function ControlClient() { }) .catch((err) => { console.error("Failed to load mission:", err); - toast.error("Failed to load mission"); + // Only show error toast if this wasn't due to auth (authRetryTrigger > 0 means we already retried) + if (authRetryTrigger > 0) { + toast.error("Failed to load mission"); + } }) .finally(() => setMissionLoading(false)); } else { @@ -893,7 +907,7 @@ export default function ControlClient() { console.error("Failed to get current mission:", err); }); } - }, [searchParams, router, missionHistoryToItems]); + }, [searchParams, router, missionHistoryToItems, authRetryTrigger]); // Poll for running parallel missions useEffect(() => { @@ -916,7 +930,7 @@ export default function ControlClient() { return () => clearInterval(interval); }, []); - // Fetch available providers and models for mission creation + // Fetch available providers and models for mission creation (retry on auth success) useEffect(() => { listProviders() .then((data) => { @@ -925,7 +939,7 @@ export default function ControlClient() { .catch((err) => { console.error("Failed to fetch providers:", err); }); - }, []); + }, [authRetryTrigger]); // Fetch server configuration (max_iterations) from health endpoint useEffect(() => { diff --git a/dashboard/src/components/auth-gate.tsx b/dashboard/src/components/auth-gate.tsx index faa71be..6e92058 100644 --- a/dashboard/src/components/auth-gate.tsx +++ b/dashboard/src/components/auth-gate.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from 'react'; import { login, getHealth } from '@/lib/api'; -import { clearJwt, getValidJwt, setJwt } from '@/lib/auth'; +import { clearJwt, getValidJwt, setJwt, signalAuthSuccess } from '@/lib/auth'; import { Lock } from 'lucide-react'; export function AuthGate({ children }: { children: React.ReactNode }) { @@ -61,6 +61,7 @@ export function AuthGate({ children }: { children: React.ReactNode }) { setJwt(res.token, res.exp); setIsAuthed(true); setPassword(''); + signalAuthSuccess(); } catch { setError('Invalid password'); } finally { diff --git a/dashboard/src/lib/auth.ts b/dashboard/src/lib/auth.ts index d41a484..9fcdfef 100644 --- a/dashboard/src/lib/auth.ts +++ b/dashboard/src/lib/auth.ts @@ -50,3 +50,8 @@ export function signalAuthRequired(): void { if (typeof window === 'undefined') return; window.dispatchEvent(new CustomEvent('openagent:auth:required')); } + +export function signalAuthSuccess(): void { + if (typeof window === 'undefined') return; + window.dispatchEvent(new CustomEvent('openagent:auth:success')); +}