MCPcopy Index your code
hub / github.com/codeaashu/claude-code / tokens

Method tokens

src/services/mcp/auth.ts:1540–1702  ·  view source on GitHub ↗
()

Source from the content-addressed store, hash-verified

1538 }
1539
1540 async tokens(): Promise<OAuthTokens | undefined> {
1541 // Cross-process token changes (another CC instance refreshed or invalidated)
1542 // are picked up via the keychain cache TTL (see macOsKeychainStorage.ts).
1543 // In-process writes already invalidate the cache via storage.update().
1544 // We do NOT clearKeychainCache() here — tokens() is called by the MCP SDK's
1545 // _commonHeaders on every request, and forcing a cache miss would trigger
1546 // a blocking spawnSync(`security find-generic-password`) 30-40x/sec.
1547 // See CPU profile: spawnSync was 7.2% of total CPU after PR #19436.
1548 const storage = getSecureStorage()
1549 const data = await storage.readAsync()
1550 const serverKey = getServerKey(this.serverName, this.serverConfig)
1551
1552 const tokenData = data?.mcpOAuth?.[serverKey]
1553
1554 // XAA: a cached id_token plays the same UX role as a refresh_token — run
1555 // the silent exchange to get a fresh access_token without a browser. The
1556 // id_token does expire (we re-acquire via `xaa login` when it does); the
1557 // point is that while it's valid, re-auth is zero-interaction.
1558 //
1559 // Only fire when we don't have a refresh_token. If the AS returned one,
1560 // the normal refresh path (below) is cheaper — 1 request vs the 4-request
1561 // XAA chain. If that refresh is revoked, refreshAuthorization() clears it
1562 // (invalidateCredentials('tokens')), and the next tokens() falls through
1563 // to here.
1564 //
1565 // Fires on:
1566 // - never authed (!tokenData) → first connect, auto-auth
1567 // - SDK partial write {accessToken:''} → stale from past session
1568 // - expired/expiring, no refresh_token → proactive XAA re-auth
1569 //
1570 // No special-casing of {accessToken:'', expiresAt:0}. Yes, SDK auth()
1571 // writes that mid-flow (saveClientInformation defaults). But with this
1572 // auto-auth branch, the *first* tokens() call — before auth() writes
1573 // anything — fires xaaRefresh. If id_token is cached, SDK short-circuits
1574 // there and never reaches the write. If id_token isn't cached, xaaRefresh
1575 // returns undefined in ~1 keychain read, auth() proceeds, writes the
1576 // marker, calls tokens() again, xaaRefresh fails again identically.
1577 // Harmless redundancy, not a wasted exchange. And guarding on `!==''`
1578 // permanently bricks auto-auth when a *prior* session left that marker
1579 // in keychain — real bug seen with xaa.dev.
1580 //
1581 // xaaRefresh() internally short-circuits to undefined when the id_token
1582 // isn't cached (or settings.xaaIdp is gone) → we fall through to the
1583 // existing needs-auth path → user runs `xaa login`.
1584 //
1585 if (
1586 isXaaEnabled() &&
1587 this.serverConfig.oauth?.xaa &&
1588 !tokenData?.refreshToken &&
1589 (!tokenData?.accessToken ||
1590 (tokenData.expiresAt - Date.now()) / 1000 <= 300)
1591 ) {
1592 if (!this._refreshInProgress) {
1593 logMCPDebug(
1594 this.serverName,
1595 tokenData
1596 ? `XAA: access_token expiring, attempting silent exchange`
1597 : `XAA: no access_token yet, attempting silent exchange`,

Callers 3

prepareServersFunction · 0.95
performMCPOAuthFlowFunction · 0.95
client.tsFile · 0.80

Calls 7

xaaRefreshMethod · 0.95
refreshAuthorizationMethod · 0.95
getSecureStorageFunction · 0.85
getServerKeyFunction · 0.85
isXaaEnabledFunction · 0.85
logMCPDebugFunction · 0.85
errorMessageFunction · 0.50

Tested by

no test coverage detected