Chapter 9 System Design

OAuth & Authentication

Token lifecycle management from login to refresh

src/utils/auth.tsLines 170
1
import chalk from 'chalk'
2
import { exec } from 'child_process'
3
import { execa } from 'execa'
4
import { mkdir, stat } from 'fs/promises'
5
import memoize from 'lodash-es/memoize.js'
6
import { join } from 'path'
7
import { CLAUDE_AI_PROFILE_SCOPE } from 'src/constants/oauth.js'
8
import {
9
  type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
10
  logEvent,
11
} from 'src/services/analytics/index.js'
12
import { getModelStrings } from 'src/utils/model/modelStrings.js'
13
import { getAPIProvider } from 'src/utils/model/providers.js'
14
import {
15
  getIsNonInteractiveSession,
16
  preferThirdPartyAuthentication,
17
} from '../bootstrap/state.js'
18
import {
19
  getMockSubscriptionType,
20
  shouldUseMockSubscription,
21
} from '../services/mockRateLimits.js'
22
import {
23
  isOAuthTokenExpired,
24
  refreshOAuthToken,
25
  shouldUseClaudeAIAuth,
26
} from '../services/oauth/client.js'
27
import { getOauthProfileFromOauthToken } from '../services/oauth/getOauthProfile.js'
28
import type { OAuthTokens, SubscriptionType } from '../services/oauth/types.js'
29
import {
30
  getApiKeyFromFileDescriptor,
31
  getOAuthTokenFromFileDescriptor,
32
} from './authFileDescriptor.js'
33
import {
34
  maybeRemoveApiKeyFromMacOSKeychainThrows,
35
  normalizeApiKeyForConfig,
36
} from './authPortable.js'
37
import {
38
  checkStsCallerIdentity,
39
  clearAwsIniCache,
40
  isValidAwsStsOutput,
41
} from './aws.js'
42
import { AwsAuthStatusManager } from './awsAuthStatusManager.js'
43
import { clearBetasCaches } from './betas.js'
44
import {
45
  type AccountInfo,
46
  checkHasTrustDialogAccepted,
47
  getGlobalConfig,
48
  saveGlobalConfig,
49
} from './config.js'
50
import { logAntError, logForDebugging } from './debug.js'
51
import {
52
  getClaudeConfigHomeDir,
53
  isBareMode,
54
  isEnvTruthy,
55
  isRunningOnHomespace,
56
} from './envUtils.js'
57
import { errorMessage } from './errors.js'
58
import { execSyncWithDefaults_DEPRECATED } from './execFileNoThrow.js'
59
import * as lockfile from './lockfile.js'
60
import { logError } from './log.js'
61
import { memoizeWithTTLAsync } from './memoize.js'
62
import { getSecureStorage } from './secureStorage/index.js'
63
import {
64
  clearLegacyApiKeyPrefetch,
65
  getLegacyApiKeyPrefetchResult,
66
} from './secureStorage/keychainPrefetch.js'
67
import {
68
  clearKeychainCache,
69
  getMacOsKeychainStorageServiceName,
70
  getUsername,
Annotations (click the dots)

src/utils/auth.ts unifies authentication across three providers: Claude.ai OAuth, AWS Bedrock, and Google Cloud. Each has different token formats and refresh semantics, but they all expose the same interface to the rest of the app.

🔑Key Insight

Tokens are stored in the OS keychain, not files. On macOS this is the Keychain; on Windows it's the credential store. This is more secure than `.env` files because keychain access requires explicit user authorization.

The refresh is proactive: a timer is set when a token is stored, firing 5 minutes before expiry. This means tokens are always fresh when a query starts — no "token expired" errors mid-conversation.

ℹ️Info

Keychain reads are prefetched at startup in parallel with other initialization. By the time the user types their first message, the auth token is already in memory — no keychain latency on the first API call.

KEY TAKEAWAYS
  • OAuth tokens are stored in the OS keychain (Keychain on macOS, credential store on Windows)
  • Token refresh is scheduled proactively — never fails mid-session due to expiry
  • PKCE (Proof Key for Code Exchange) prevents authorization code interception attacks
  • The auth module handles Claude.ai, AWS Bedrock, and Google Cloud credentials uniformly
  • Keychain access is prefetched at startup in parallel with module loading
AI Assistant

Ask anything about OAuth & Authentication

Powered by Groq · Enter to send, Shift+Enter for newline