anthropics / anthropics/claude-code

[BUG] Chrome cookie import fails: Safe Storage key queried in data-protection keychain, but Chrome stores it in the legacy login keychain (macOS)

Open
#95,088 0 comments 0 reactions 0 assignees View on GitHub
area:cowork bug has repro platform:macos
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

## Summary

The built-in browser's "Import cookies from your browser" feature fails for Google Chrome on macOS with:

> Couldn't read Google Chrome's data. Quit Google Chrome and try again.

The error message is misleading. Chrome running is not the cause, and quitting Chrome does not help. The actual failure is a **keychain routing mismatch**: the app looks up Chrome's `Chrome Safe Storage` AES key in the **data-protection keychain** (`secd`), but Chrome stores that item in the **legacy file-based login keychain** (`securityd`, `login.keychain-db`). The lookup returns `errSecItemNotFound` (-25300), the app cannot derive the AES key, and every `v10` cookie fails to decrypt.

A Touch ID prompt appears and succeeds during the attempt, which makes this look like a permissions problem. It is not — the app authenticates against a `SecAccessControl` it constructed for the data-protection keychain, and then finds nothing there.

## Environment

| | |
|---|---|
| Claude desktop (Cowork) | 2.110.1 (`com.anthropic.claudefordesktop`) |
| macOS | 27.0 (build 26A428) |
| Hardware | Apple silicon (arm64) |
| Google Chrome | 153.0.8010.48 |
| Chrome profile | `Default` (only profile present) |
| Cookie DB | `~/Library/Application Support/Google/Chrome/Default/Cookies`, 360448 bytes |
| Cookie encryption | `v10` × 675 (no `v20` / no app-bound encryption) |

## Steps to reproduce

1. Use Chrome on macOS as the primary browser so `Chrome Safe Storage` is created in the login keychain.
2. Open the Claude desktop app → built-in browser → **Import cookies from your browser**.
3. Source: **Google Chrome**, profile: **Default**. Start the import.
4. Authenticate at the Touch ID prompt (reason string: *"list the sites you're signed into in Google Chrome, to bring them into Cowork's browser"*). Authentication succeeds.
5. Import fails with *"Couldn't read Google Chrome's data. Quit Google Chrome and try again."*

Reproduces identically whether Chrome is running or fully quit.

## Expected vs actual

**Expected:** cookies are read from the `Default` profile, decrypted using the `Chrome Safe Storage` key from the login keychain, and imported.

**Actual:** import fails. The keychain lookup misses, no decryption is attempted, and the user is shown an error that blames Chrome running — an unrelated and non-reproducing condition.

## Evidence

### 1. The item is in the legacy keychain only

```
$ security find-generic-password -s "Chrome Safe Storage"
keychain: "/Users//Library/Keychains/login.keychain-db"
class: "genp"
"acct"="Chrome"
"svce"="Chrome Safe Storage"
```

Same query issued both ways via `SecItemCopyMatching` (attributes only, no secret read):

```
LEGACY (kSecUseDataProtectionKeychain=FALSE) -> 0 (errSecSuccess)
DATA-PROTECTION (=TRUE) -> -25300 (errSecItemNotFound)
```

### 2. The app only ever talks to the data-protection keychain

Unified-log capture across the whole import (`log stream --level debug`), counting keychain daemon operations attributed to the app's main process:

```
secd[632] (data-protection keychain) ops from Claude[2394]: 6
securityd[391] (legacy login keychain) ops from Claude[2394]: 0
```

Every operation takes the `_ios` / data-protection path:

```
Claude[2394]: (Security) Created Activity ID: 0x4fc05, Description: SecItemCopyMatching
Claude[2394]: (Security) Created Activity ID: 0x4fc06, Description: SecItemCopyMatching_ios
Claude[2394]: (Security) [com.apple.securityd:xpc] not using system keychain
secd[632]: [com.apple.securityd:serverxpc] XPC [Claude[2394]/1#13 LF=0] operation: copy_matching (1)
```

The legacy keychain daemon `securityd[391]` is never consulted by the app. (The only `securityd[391]` entries mentioning "claude" in the capture belong to the bundled `claude-code` CLI at an unrelated PID, from the investigation tooling itself.)

### 3. Touch ID succeeds, against the wrong keychain

```
Claude[2394]: (LocalAuthentication) [Client,Interactive] evaluateAccessControl:

operation:3 options:{
1096 = 1;
2 = "list the sites you\U2019re signed into in Google Chrome, to bring them into Cowork\U2019s browser";
}

coreautha[1605]: [com.apple.LocalAuthentication:UI] ... {
CallerId = "com.anthropic.claudefordesktop";
CallerName = Claude;
Constraint = { cpo = DeviceOwnerAuthentication; };
ConstraintOp = osgn;
}

Claude[2394]: (LocalAuthentication) evaluateAccessControl on LAContext[...] returned {
14 = 1;
1 = 1;
8 = 501;
} <-- success
```

`aku` and `SecAccessControl` are data-protection keychain constructs. Chrome's item is a legacy `genp` entry guarded by a keychain ACL, not by a `SecAccessControl`. After this successful authentication at `13:49:22.07`, the app issues **no further keychain operation of any kind** — it does not retry against the legacy keychain.

### 4. Everything the error message implicates is healthy

Chrome running does not lock the database. With Chrome fully running (33 processes, network service holding the file open):

```
sqlite3 "file:.../Default/Cookies?mode=ro" "SELECT COUNT(*) FROM cookies;" -> 675 exit=0
sqlite3 ".../Default/Cookies" "BEGIN IMMEDIATE; SELECT 1; COMMIT;" -> 1 exit=0
```

`BEGIN IMMEDIATE` acquires an exclusive write lock successfully, so no reader is being blocked.

Database is well-formed (checked on a copy, never the original):

```
PRAGMA integrity_check; -> ok
675 cookies across 186 distinct hosts, 0 rows with empty encrypted_value
encrypted_value prefix distribution: v10 = 675
```

No app-bound encryption: `Local State` has no `os_crypt` section at all, and no `app_bound_encrypted_key`.

Ruled out by the capture as well:
- **No TCC denial.** No `tccd` denial for `com.anthropic.claudefordesktop` anywhere in 41,152 captured lines.
- **No sandbox denial.** The app is not App-Sandboxed (no `com.apple.security.app-sandbox`); no `Sandbox: ... deny` line names it.
- **Not `errSecAuthFailed` / `errSecInteractionNotAllowed`.** Authentication returned success.
- **Not wrong path or wrong profile.** `Default` is the only profile and does contain the populated `Cookies` DB.

## Secondary issues

**No app-side diagnostics.** Nothing about the import is logged. `~/Library/Logs/Claude/in-app-browser.log` contains only an unrelated React hydration warning. Grepping `main.log` (4.4 MB), `coworkd.log`, `swift.log` and `claude.ai-web.log` for `cookie|safe.?storage|keychain|import|decrypt` returns zero hits. The failing path should log the `OSStatus` it got back.

**The error message misdirects.** "Quit Google Chrome and try again" sends users down a dead end for a failure that has nothing to do with Chrome running. It should distinguish "couldn't find the decryption key in your keychain" from "couldn't open the cookie database".

**Possible follow-on.** Full Disk Access on this machine is held by `com.anthropic.claude-code`, not by `com.anthropic.claudefordesktop`; the desktop app has no `kTCCServiceSystemPolicyAllFiles` entry. This did **not** cause the reported failure — no TCC denial was logged, because the import fails at the keychain step before file access matters. But it may surface as a second failure once the keychain bug is fixed, and it is worth noting that both bundles appear as "Claude" in System Settings, which makes granting the wrong one easy.

## Suggested fix

Query the legacy keychain for `Chrome Safe Storage` — either `SecKeychainFindGenericPassword`, or `SecItemCopyMatching` with `kSecUseDataProtectionKeychain = false` — with service `Chrome Safe Storage` and account `Chrome`. Chromium's own `OSCrypt` on macOS uses the legacy keychain, so the import must match it. Falling back from the data-protection keychain to the legacy keychain on `errSecItemNotFound` would also work and would cover Chromium variants that store the key elsewhere. The same mismatch likely affects Brave, Edge, Vivaldi and other Chromium browsers, which use the same scheme with different service names.

---

*Log excerpts trimmed to the relevant subsystems. Cookie values, hostnames, account identifiers, audit tokens, authentication blobs, metrics client IDs and LAN addresses are excluded; only aggregate counts are reported. Usernames replaced with ``.*

Contributor guide

No contributing guide indexed for this repository

Research direction

Locate the macOS browser cookie import path and the keychain lookup it uses for Chrome Safe Storage; the issue provides no source files or tests. Verify the lookup against Chrome's legacy login keychain, then test that v10 cookies import successfully and that keychain and database failures produce distinct diagnostics.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos, sqlite
Domain
desktop-dev, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.