@@ -49,8 +49,6 @@ export function useAuth() {
4949 const [ refreshEnabled , setRefreshEnabled ] = useState ( false ) ;
5050 const [ sessionExpired , setSessionExpired ] = useState ( false ) ;
5151 const isRefreshing = useRef ( false ) ;
52- const refreshTimer = useRef < ReturnType < typeof setTimeout > > ( null ) ;
53- const expiryTimer = useRef < ReturnType < typeof setTimeout > > ( null ) ;
5452 const refreshDeadline = useRef < ReturnType < typeof setTimeout > > ( null ) ;
5553
5654 // Effective expiry of the current session (0 = no session).
@@ -95,6 +93,12 @@ export function useAuth() {
9593 isRefreshing . current = false ;
9694 } , [ ] ) ;
9795
96+ /** Renewal failed without a hub verdict — settle and stand One Tap down. */
97+ const abandonRenewal = useCallback ( ( ) => {
98+ settleRefresh ( ) ;
99+ setRefreshEnabled ( false ) ;
100+ } , [ settleRefresh ] ) ;
101+
98102 // Single entry point for activating One Tap. Coalesces concurrent
99103 // triggers (e.g. N parallel 401s) into one refresh attempt.
100104 const triggerRefresh = useCallback ( ( ) => {
@@ -104,11 +108,8 @@ export function useAuth() {
104108 // The IdP may never call back (GIS blocked / FedCM policies). Without a
105109 // deadline, isRefreshing wedges true for the tab's lifetime: the expiry
106110 // re-check never reaches a verdict and refocus/retry are disabled.
107- refreshDeadline . current = setTimeout ( ( ) => {
108- settleRefresh ( ) ;
109- setRefreshEnabled ( false ) ;
110- } , REFRESH_VERDICT_TIMEOUT_MS ) ;
111- } , [ settleRefresh ] ) ;
111+ refreshDeadline . current = setTimeout ( abandonRenewal , REFRESH_VERDICT_TIMEOUT_MS ) ;
112+ } , [ abandonRenewal ] ) ;
112113
113114 // Silent renewal via the active AuthProvider. The provider collapses
114115 // "renewal returned no usable credential" into onError, so the consumer
@@ -133,8 +134,7 @@ export function useAuth() {
133134 setRefreshEnabled ( false ) ;
134135 } ,
135136 onError : ( ) => {
136- settleRefresh ( ) ;
137- setRefreshEnabled ( false ) ;
137+ abandonRenewal ( ) ;
138138 if ( sessionLapsed ( ) ) expireSession ( ) ;
139139 } ,
140140 } ) ;
@@ -180,9 +180,6 @@ export function useAuth() {
180180 // Schedule silent refresh and an expiry-time server re-check from the
181181 // session's real expiry.
182182 useEffect ( ( ) => {
183- if ( refreshTimer . current ) clearTimeout ( refreshTimer . current ) ;
184- if ( expiryTimer . current ) clearTimeout ( expiryTimer . current ) ;
185-
186183 if ( ! auth ) {
187184 expiresAtRef . current = 0 ;
188185 return ;
@@ -192,15 +189,16 @@ export function useAuth() {
192189 expiresAtRef . current = expiresAt ;
193190
194191 // Silent refresh before expiry; immediately if already inside the buffer.
195- refreshTimer . current = setTimeout (
192+ const refreshTimer = setTimeout (
196193 triggerRefresh ,
197194 Math . max ( expiresAt - REFRESH_BUFFER_MS - Date . now ( ) , 0 ) ,
198195 ) ;
199196
200197 // Expiry-time re-check. Only a definitive 401/403 clears the session;
201198 // network errors reschedule (logout on evidence, not on schedule).
199+ let expiryTimer : ReturnType < typeof setTimeout > ;
202200 const scheduleExpiryCheck = ( delay : number ) => {
203- expiryTimer . current = setTimeout ( ( ) => {
201+ expiryTimer = setTimeout ( ( ) => {
204202 fetchAuthMe ( )
205203 . then ( ( me ) => {
206204 if ( me ) {
@@ -222,8 +220,8 @@ export function useAuth() {
222220 scheduleExpiryCheck ( msUntilExpiry > 0 ? msUntilExpiry + 1000 : EXPIRY_RECHECK_MS ) ;
223221
224222 return ( ) => {
225- if ( refreshTimer . current ) clearTimeout ( refreshTimer . current ) ;
226- if ( expiryTimer . current ) clearTimeout ( expiryTimer . current ) ;
223+ clearTimeout ( refreshTimer ) ;
224+ clearTimeout ( expiryTimer ) ;
227225 } ;
228226 } , [ auth , applyAuth , expireSession , triggerRefresh ] ) ;
229227
0 commit comments