# How CodexBar's Keychain Access System Retrieves Browser Cookies on macOS

> Learn how CodexBar retrieves browser cookies on macOS. It accesses Keychain, decrypts your AES-256-GCM protected database, and caches HTTP headers for quick reuse.

- Repository: [Peter Steinberger/CodexBar](https://github.com/steipete/CodexBar)
- Tags: internals
- Published: 2026-07-08

---

**CodexBar retrieves browser cookies by executing non-interactive Keychain reads to obtain the browser's encryption key, decrypting the AES-256-GCM protected SQLite cookie database, and caching the resulting HTTP headers in a dedicated Keychain item for fast reuse.**

CodexBar, an open-source macOS menu bar application maintained by **steipete/CodexBar**, implements a sophisticated Keychain access system to authenticate with AI providers like OpenAI and Claude without requiring manual API key entry. The system extracts the encryption key that browsers store in the macOS Keychain, uses it to decrypt the local `Cookies` SQLite database, and transforms the encrypted values into standard HTTP `Cookie` headers.

## The Seven-Step Cookie Retrieval Pipeline

The CodexBar Keychain access system follows a deterministic pipeline to move from raw Keychain data to usable authentication headers.

### Step 1: Locating the Browser's Safe Storage Entry

CodexBar prioritizes **Chrome** when retrieving credentials. It queries the macOS Security Framework for the *"Chrome Safe Storage"* entry, falling back to Safari's equivalent storage if Chrome is unavailable. This logic resides in **[`KeychainPromptCoordinator.swift`](https://github.com/steipete/CodexBar/blob/main/KeychainPromptCoordinator.swift)**, which builds the user-facing alert model while the underlying system locates the storage item.

### Step 2: Non-Interactive Keychain Reads

To avoid interrupting the user with system dialogs, CodexBar performs all Keychain operations with **`kSecUseAuthenticationUIFail`**. This flag instructs `SecItemCopyMatching` to return an error rather than prompt for authentication if the Keychain is locked.

In **[`KeychainCacheStore.swift`](https://github.com/steipete/CodexBar/blob/main/KeychainCacheStore.swift)**, the wrapper calls `SecItemCopyMatching` with **`kSecAttrAccessibleAfterFirstUnlock`** and **`kSecUseAuthenticationUIFail`**, ensuring the app reads the internet-password item containing the browser's master key without UI interaction. If the OS requires authentication, the read fails silently and CodexBar presents a friendly alert.

### Step 3: Deriving the AES-256 Encryption Key

The raw bytes retrieved from the Keychain represent the master key used by Chromium's "OS-crypt" implementation. **[`ChromeCookieKeyProvider.swift`](https://github.com/steipete/CodexBar/blob/main/ChromeCookieKeyProvider.swift)** transforms this payload into a usable **AES-256-GCM** key following the public Chromium specification (as implemented in `os_crypt.cc`). This cryptographic key is required to decrypt the individual cookie values stored in the browser's SQLite database.

### Step 4: Querying the SQLite Cookie Database

With the encryption key in hand, **[`ChromeCookieImporter.swift`](https://github.com/steipete/CodexBar/blob/main/ChromeCookieImporter.swift)** opens the browser's `Cookies` file (located at `<profile>/Cookies` or `<profile>/Network/Cookies` in newer versions) using SQLite. The code executes a targeted `SELECT` statement that filters rows by `host_key`, retrieving only cookies matching the specific provider domain (e.g., `*.openai.com` or `*.claude.ai`).

### Step 5: Decrypting Cookie Values

For each row returned by the database query, the `encrypted_value` column contains AES-256-GCM encrypted data. **[`ChromeCookieImporter.swift`](https://github.com/steipete/CodexBar/blob/main/ChromeCookieImporter.swift)** decrypts these values using the key derived in Step 3, producing plaintext strings like `session=abc123; path=/; domain=.openai.com`.

### Step 6: Caching Headers in the Keychain

To eliminate repeated decryption overhead and reduce database access, CodexBar stores the assembled HTTP header string in its own Keychain item under the service identifier **`com.steipete.codexbar.cache`**. The **[`CookieHeaderCache.swift`](https://github.com/steipete/CodexBar/blob/main/CookieHeaderCache.swift)** module writes the header to an account named `cookie.<provider>` (e.g., `cookie.openai`), enabling fast, read-only retrieval for subsequent requests until the session expires.

### Step 7: Providing Headers to API Providers

When a provider implementation (such as [`OpenAIWebProvider.swift`](https://github.com/steipete/CodexBar/blob/main/OpenAIWebProvider.swift) or [`ClaudeOAuthProvider.swift`](https://github.com/steipete/CodexBar/blob/main/ClaudeOAuthProvider.swift)) requires authentication, it calls `CookieHeaderCache.load(provider:)`. If a valid cached entry exists, it is injected directly into the HTTP request; otherwise, the full import pipeline triggers automatically.

## Security Configuration and Browser Fallback

The Keychain access system implements several security-first policies to balance functionality with user privacy.

**Read-Only, Non-Interactive Design.** All Keychain reads use `kSecUseAuthenticationUIFail`. If the system cannot return the key without UI (for example, if the user has locked their Keychain), CodexBar aborts the import and displays a preferences alert rather than forcing a system dialog.

**Browser Fallback Order.** The import logic attempts Chrome first, then Chromium-based variants, then Safari. The code walks this list sequentially and stops at the first readable store, ensuring maximum compatibility without unnecessary Keychain queries.

**Granular User Controls.** Users can disable Keychain access entirely via *Preferences → Advanced → Disable Keychain Access*, stored in **[`Settings.swift`](https://github.com/steipete/CodexBar/blob/main/Settings.swift)** as the `disableKeychainAccess` boolean. Individual providers can also implement per-domain prompt policies.

## Implementation Examples

### Importing Chrome Cookies for a Provider

```swift
import CodexBar

/// Import a cookie header for the given provider domain.
func importChromeCookie(for provider: Provider) async throws -> String {
    // 1️⃣ Locate the Chrome Safe Storage entry.
    let rawKey = try KeychainCacheStore.readInternetPassword(
        service: "Chrome Safe Storage"
    )
    
    // 2️⃣ Derive the AES‑GCM key.
    let cryptoKey = ChromeCookieKeyProvider.key(from: rawKey)
    
    // 3️⃣ Open the Chrome profile's Cookies DB.
    let importer = ChromeCookieImporter(profilePath: .defaultChrome, key: cryptoKey)
    
    // 4️⃣ Pull & decrypt the matching cookies.
    let header = try await importer.cookieHeader(for: provider.cookieDomain)
    
    // 5️⃣ Cache the result for later fast reads.
    CookieHeaderCache.store(provider: provider, cookieHeader: header, sourceLabel: "Chrome")
    
    return header
}

```

*Key files referenced:* [`KeychainCacheStore.swift`](https://github.com/steipete/CodexBar/blob/main/KeychainCacheStore.swift), [`ChromeCookieKeyProvider.swift`](https://github.com/steipete/CodexBar/blob/main/ChromeCookieKeyProvider.swift), [`ChromeCookieImporter.swift`](https://github.com/steipete/CodexBar/blob/main/ChromeCookieImporter.swift), [`CookieHeaderCache.swift`](https://github.com/steipete/CodexBar/blob/main/CookieHeaderCache.swift).

### Using a Cached Cookie Header

```swift
import CodexBar

func makeRequest(to endpoint: URL, using provider: Provider) async throws -> Data {
    // Load a cached header; if nil, start an import.
    let header = CookieHeaderCache.load(provider: provider) ??
                 try await importChromeCookie(for: provider)
    
    var request = URLRequest(url: endpoint)
    request.addValue(header, forHTTPHeaderField: "Cookie")
    
    let (data, _) = try await URLSession.shared.data(for: request)
    return data
}

```

*Key file referenced:* [`CookieHeaderCache.swift`](https://github.com/steipete/CodexBar/blob/main/CookieHeaderCache.swift).

### Disabling Keychain Access Programmatically

```swift
import CodexBar

/// Turn off all keychain reads for the running instance.
func disableKeychainAccess() {
    Settings.shared.disableKeychainAccess = true
    // The next import will short-circuit and show an alert.
}

```

*Key file referenced:* [`Settings.swift`](https://github.com/steipete/CodexBar/blob/main/Settings.swift).

## Summary

- **Non-interactive retrieval:** CodexBar uses `SecItemCopyMatching` with `kSecUseAuthenticationUIFail` to read browser keys from the macOS Keychain without prompting the user.
- **AES-256-GCM decryption:** The system derives the Chromium master key from Keychain data and uses it to decrypt cookies from the SQLite `Cookies` database.
- **Intelligent caching:** Decrypted headers are stored in `com.steipete.codexbar.cache` to eliminate redundant database access and cryptographic operations.
- **Browser flexibility:** The implementation supports Chrome, Chromium variants, and Safari through a prioritized fallback mechanism.
- **User control:** Global and per-provider settings allow users to disable Keychain access entirely via [`Settings.swift`](https://github.com/steipete/CodexBar/blob/main/Settings.swift).

## Frequently Asked Questions

### Is CodexBar's Keychain access safe for my browser data?

Yes. The CodexBar Keychain access system operates read-only and never modifies browser databases. It uses standard macOS Security Framework APIs with `kSecUseAuthenticationUIFail` to prevent unauthorized access attempts, and caches decrypted headers only in a dedicated Keychain item (`com.steipete.codexbar.cache`) separate from browser storage.

### Why does CodexBar need to access the "Chrome Safe Storage" Keychain item?

Modern browsers encrypt their cookie databases using a master key stored in the macOS Keychain under names like "Chrome Safe Storage." CodexBar must read this key via `SecItemCopyMatching` in **[`KeychainCacheStore.swift`](https://github.com/steipete/CodexBar/blob/main/KeychainCacheStore.swift)** to derive the AES-256-GCM key required to decrypt the `encrypted_value` columns in the browser's SQLite database.

### What happens if I deny Keychain access or disable it in preferences?

If Keychain access is disabled via `Settings.shared.disableKeychainAccess` or the system denies the read, CodexBar aborts the import pipeline and displays a preferences alert. The app cannot retrieve browser cookies without this access, meaning users must either enable Keychain access or manually provide authentication credentials through other means.

### Does CodexBar support retrieving cookies from browsers other than Chrome?

Yes. While CodexBar prioritizes Chrome, the system implements a browser fallback order (Chrome → Chromium-based → Safari) within the import logic. If **[`ChromeCookieImporter.swift`](https://github.com/steipete/CodexBar/blob/main/ChromeCookieImporter.swift)** cannot locate a Chrome profile, the system attempts to read from Safari's Safe Storage, ensuring compatibility across the major macOS browsers.