@@ -18,23 +18,11 @@ export function useAuth(): UseAuthReturn {
1818 let cancelled = false ;
1919
2020 async function init ( ) {
21- // Determine if password is required (uses localStorage cache when server unreachable)
22- const config = await getConfig ( ) ;
23- if ( cancelled ) return ;
24- setPasswordRequired ( config . passwordRequired ) ;
25-
26- // No-password mode: skip auth checks entirely — always grant access
27- if ( ! config . passwordRequired ) {
28- setAuthenticated ( true ) ;
29- return ;
30- }
31-
32- // Check for one-time-token in URL
21+ // Check for one-time-token in URL first
3322 const params = new URLSearchParams ( window . location . search ) ;
3423 const ott = params . get ( 'ott' ) ;
3524
3625 if ( ott ) {
37- // Remove ott from URL without reload
3826 params . delete ( 'ott' ) ;
3927 const search = params . toString ( ) ;
4028 const newUrl =
@@ -59,17 +47,37 @@ export function useAuth(): UseAuthReturn {
5947 }
6048 }
6149
62- try {
63- const { authenticated : isAuth , serverReachable } = await checkAuth ( ) ;
64- if ( ! cancelled ) {
65- if ( ! isAuth && ! serverReachable ) {
66- setAuthenticated ( false ) ;
67- return ;
68- }
69- setAuthenticated ( isAuth ) ;
70- }
71- } catch {
72- if ( ! cancelled ) setAuthenticated ( false ) ;
50+ // Primary check: can we reach the server and are we authenticated?
51+ const { authenticated : isAuth , serverReachable } = await checkAuth ( ) ;
52+ if ( cancelled ) return ;
53+
54+ if ( isAuth ) {
55+ setAuthenticated ( true ) ;
56+ return ;
57+ }
58+
59+ // Not authenticated — but is a password even required?
60+ // If server is reachable and returned 401, password is required.
61+ // If server is unreachable (tunnel stale, network down), check /api/config.
62+ if ( serverReachable ) {
63+ // Server responded with 401 — password is required
64+ setPasswordRequired ( true ) ;
65+ setAuthenticated ( false ) ;
66+ return ;
67+ }
68+
69+ // Server unreachable — check if password is configured.
70+ // /api/config has no auth middleware, so it works even without a token.
71+ const config = await getConfig ( ) ;
72+ if ( cancelled ) return ;
73+ setPasswordRequired ( config . passwordRequired ) ;
74+
75+ if ( ! config . passwordRequired ) {
76+ // No password mode + server unreachable (tunnel flaky) — grant access.
77+ // The terminal/sessions hub will show its own connection banner.
78+ setAuthenticated ( true ) ;
79+ } else {
80+ setAuthenticated ( false ) ;
7381 }
7482 }
7583
@@ -81,24 +89,12 @@ export function useAuth(): UseAuthReturn {
8189
8290 // Re-check auth when returning from background (e.g. mobile tab switch after hours idle).
8391 useEffect ( ( ) => {
84- let retryTimer : ReturnType < typeof setTimeout > | null = null ;
85-
8692 function handleVisibility ( ) {
8793 if ( document . hidden ) return ;
8894
8995 if ( ! passwordRequired ) {
90- // No-password mode: server is always "authenticated", but verify reachability.
91- // If unreachable, keep authenticated=true — the terminal/sessions hub will
92- // show its own connection banner. Once reachable again, everything auto-recovers.
93- checkAuth ( ) . then ( ( { serverReachable } ) => {
94- if ( ! serverReachable && retryTimer === null ) {
95- // Schedule a silent retry in case tunnel just needs a moment
96- retryTimer = setTimeout ( ( ) => {
97- retryTimer = null ;
98- checkAuth ( ) ; // fire-and-forget; UI stays on terminal
99- } , 5000 ) ;
100- }
101- } ) ;
96+ // No-password mode: never flip to unauthenticated — the terminal handles
97+ // connection issues with its own reconnect banner.
10298 return ;
10399 }
104100
@@ -110,10 +106,7 @@ export function useAuth(): UseAuthReturn {
110106 }
111107
112108 document . addEventListener ( 'visibilitychange' , handleVisibility ) ;
113- return ( ) => {
114- document . removeEventListener ( 'visibilitychange' , handleVisibility ) ;
115- if ( retryTimer !== null ) clearTimeout ( retryTimer ) ;
116- } ;
109+ return ( ) => document . removeEventListener ( 'visibilitychange' , handleVisibility ) ;
117110 } , [ authenticated , passwordRequired ] ) ;
118111
119112 const login = useCallback ( async ( password : string ) : Promise < boolean > => {
0 commit comments