11import { useState , useEffect , useCallback } from 'react' ;
2- import { checkAuth , login as apiLogin , logout as apiLogout } from '@/services/api' ;
2+ import { checkAuth , getConfig , login as apiLogin , logout as apiLogout } from '@/services/api' ;
33
44interface UseAuthReturn {
55 authenticated : boolean | null ;
6+ passwordRequired : boolean ;
67 login : ( password : string ) => Promise < boolean > ;
78 logout : ( ) => Promise < void > ;
89 loading : boolean ;
910}
1011
1112export function useAuth ( ) : UseAuthReturn {
1213 const [ authenticated , setAuthenticated ] = useState < boolean | null > ( null ) ;
14+ const [ passwordRequired , setPasswordRequired ] = useState ( true ) ;
1315 const [ loading , setLoading ] = useState ( false ) ;
1416
1517 useEffect ( ( ) => {
1618 let cancelled = false ;
1719
1820 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+
1932 // Check for one-time-token in URL
2033 const params = new URLSearchParams ( window . location . search ) ;
2134 const ott = params . get ( 'ott' ) ;
@@ -50,8 +63,6 @@ export function useAuth(): UseAuthReturn {
5063 const { authenticated : isAuth , serverReachable } = await checkAuth ( ) ;
5164 if ( ! cancelled ) {
5265 if ( ! isAuth && ! serverReachable ) {
53- // Server unreachable — show login page so user sees something
54- // (a full reload could loop if the server is genuinely down)
5566 setAuthenticated ( false ) ;
5667 return ;
5768 }
@@ -69,17 +80,41 @@ export function useAuth(): UseAuthReturn {
6980 } , [ ] ) ;
7081
7182 // Re-check auth when returning from background (e.g. mobile tab switch after hours idle).
72- // Catches expired tokens / stale DevTunnel sessions that would otherwise show a white screen.
7383 useEffect ( ( ) => {
84+ let retryTimer : ReturnType < typeof setTimeout > | null = null ;
85+
7486 function handleVisibility ( ) {
75- if ( document . hidden || authenticated !== true ) return ;
87+ if ( document . hidden ) return ;
88+
89+ 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+ } ) ;
102+ return ;
103+ }
104+
105+ // Password mode: if we were authenticated and now we're not, show login
106+ if ( authenticated !== true ) return ;
76107 checkAuth ( ) . then ( ( { authenticated : isAuth } ) => {
77108 if ( ! isAuth ) setAuthenticated ( false ) ;
78109 } ) ;
79110 }
111+
80112 document . addEventListener ( 'visibilitychange' , handleVisibility ) ;
81- return ( ) => document . removeEventListener ( 'visibilitychange' , handleVisibility ) ;
82- } , [ authenticated ] ) ;
113+ return ( ) => {
114+ document . removeEventListener ( 'visibilitychange' , handleVisibility ) ;
115+ if ( retryTimer !== null ) clearTimeout ( retryTimer ) ;
116+ } ;
117+ } , [ authenticated , passwordRequired ] ) ;
83118
84119 const login = useCallback ( async ( password : string ) : Promise < boolean > => {
85120 setLoading ( true ) ;
@@ -88,7 +123,6 @@ export function useAuth(): UseAuthReturn {
88123 setAuthenticated ( ok ) ;
89124 return ok ;
90125 } catch ( err ) {
91- // Re-throw so caller can distinguish 429 from other errors
92126 throw err ;
93127 } finally {
94128 setLoading ( false ) ;
@@ -100,5 +134,5 @@ export function useAuth(): UseAuthReturn {
100134 setAuthenticated ( false ) ;
101135 } , [ ] ) ;
102136
103- return { authenticated, login, logout, loading } ;
137+ return { authenticated, passwordRequired , login, logout, loading } ;
104138}
0 commit comments