MCPcopy
hub / github.com/codeaashu/claude-code / recoverFromAuthFailure

Function recoverFromAuthFailure

src/bridge/remoteBridgeCore.ts:530–590  ·  view source on GitHub ↗
()

Source from the content-addressed store, hash-verified

528
529 // ── 8. 401 recovery (OAuth refresh + rebuild) ───────────────────────────
530 async function recoverFromAuthFailure(): Promise<void> {
531 // setOnClose already guards `!authRecoveryInFlight` but that check and
532 // this set must be atomic against onRefresh — claim synchronously before
533 // any await. Laptop wake fires both paths ~simultaneously.
534 if (authRecoveryInFlight) return
535 authRecoveryInFlight = true
536 onStateChange?.('reconnecting', 'JWT expired — refreshing')
537 logForDebugging('[remote-bridge] 401 on SSE — attempting JWT refresh')
538 try {
539 // Unconditionally try OAuth refresh — getAccessToken() returns expired
540 // tokens as non-null strings, so !oauthToken doesn't catch expiry.
541 // Pass the stale token so handleOAuth401Error's keychain-comparison
542 // can detect if another tab already refreshed.
543 const stale = getAccessToken()
544 if (onAuth401) await onAuth401(stale ?? '')
545 const oauthToken = getAccessToken() ?? stale
546 if (!oauthToken || tornDown) {
547 if (!tornDown) {
548 onStateChange?.('failed', 'JWT refresh failed: no OAuth token')
549 }
550 return
551 }
552
553 const fresh = await withRetry(
554 () =>
555 fetchRemoteCredentials(
556 sessionId,
557 baseUrl,
558 oauthToken,
559 cfg.http_timeout_ms,
560 ),
561 'fetchRemoteCredentials (recovery)',
562 cfg,
563 )
564 if (!fresh || tornDown) {
565 if (!tornDown) {
566 onStateChange?.('failed', 'JWT refresh failed after 401')
567 }
568 return
569 }
570 // If 401 interrupted the initial flush, writeBatch may have silently
571 // no-op'd on the closed uploader (ccr.close() ran in the SSE wrapper
572 // before our setOnClose callback). Reset so the new onConnect re-flushes.
573 // (v1 scopes initialFlushDone inside the per-transport closure at
574 // replBridge.ts:1027 so it resets naturally; v2 has it at outer scope.)
575 initialFlushDone = false
576 await rebuildTransport(fresh, 'auth_401_recovery')
577 logForDebugging('[remote-bridge] Transport rebuilt after 401')
578 } catch (err) {
579 logForDebugging(
580 `[remote-bridge] 401 recovery failed: ${errorMessage(err)}`,
581 { level: 'error' },
582 )
583 logForDiagnosticsNoPII('error', 'bridge_repl_v2_jwt_refresh_failed')
584 if (!tornDown) {
585 onStateChange?.('failed', `JWT refresh failed: ${errorMessage(err)}`)
586 }
587 } finally {

Callers 1

wireTransportCallbacksFunction · 0.85

Calls 8

onStateChangeFunction · 0.85
logForDebuggingFunction · 0.85
getAccessTokenFunction · 0.85
rebuildTransportFunction · 0.85
logForDiagnosticsNoPIIFunction · 0.85
withRetryFunction · 0.70
fetchRemoteCredentialsFunction · 0.70
errorMessageFunction · 0.50

Tested by

no test coverage detected